Java多線程學(xué)習(xí)(三)——synchronized(下)

synchronized同步語句塊

用關(guān)鍵字synchronized聲明方法是有弊端的小泉。比如線程A調(diào)用同步方法執(zhí)行一個長時間任務(wù)瞭空,那么線程B就要等較長時間才能調(diào)用冕屯。

下面看一個例子:

public class Task {

    private String getData1;

    private String getData2;

    public synchronized void longTimeTask(){
        try {
            System.out.println("begin task");
            Thread.sleep(3000);
            getData1 = "長時間處理任務(wù)后從遠(yuǎn)程返回的值1 threadName=" + Thread.currentThread().getName();
            getData2 = "長時間處理任務(wù)后從遠(yuǎn)程返回的值2 threadName=" + Thread.currentThread().getName();
            System.out.println(getData1);
            System.out.println(getData2);
            System.out.println("end task");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

}
public class Utils {
    public static long begainTime1;
    public static long endTime1;
    public static long begainTime2;
    public static long endTime2;


}
public class MyThread extends Thread{
    private Task task;
    private String name;

    public MyThread(Task task, String name){
        super();
        this.task=task;
        this.name=name;
        super.setName(name);
    }

    @Override
    public void run() {
        super.run();
        if ("A".equals(name)){
            Utils.begainTime1 = System.currentTimeMillis();
            task.longTimeTask();
            Utils.endTime1 = System.currentTimeMillis();
        }else {
            Utils.begainTime2 = System.currentTimeMillis();
            task.longTimeTask();
            Utils.endTime2 = System.currentTimeMillis();
        }
    }
}
public class Main {
    public static void main(String[] args){
        Task task = new Task();
        MyThread myThread = new MyThread(task, "A");
        MyThread myThread1 = new MyThread(task, "B");
        myThread.start();
        myThread1.start();
        try {
            Thread.sleep(10000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        long beginTime = Utils.begainTime1;
        if (Utils.begainTime2<Utils.begainTime1){
            beginTime = Utils.begainTime2;
        }
        long endTime = Utils.endTime1;
        if (Utils.endTime2>Utils.endTime1){
            endTime = Utils.endTime2;
        }
        System.out.println("耗時:" + (endTime-beginTime)/1000 + "s");


    }


}

輸出內(nèi)容:

begin task
長時間處理任務(wù)后從遠(yuǎn)程返回的值1 threadName=A
長時間處理任務(wù)后從遠(yuǎn)程返回的值2 threadName=A
end task
begin task
長時間處理任務(wù)后從遠(yuǎn)程返回的值1 threadName=B
長時間處理任務(wù)后從遠(yuǎn)程返回的值2 threadName=B
end task
耗時:6s

從運(yùn)行時間上來看萨驶,synchronized方法的問題很明顯驻子∧奔酰可以使用synchronized同步塊來解決這個問題牡彻。但是要注意synchronized同步塊的使用方式,如果synchronized同步塊使用不好的話并不會帶來效率的提升。

將上文的Task.class文件修改如下:

public void longTimeTask(){
        try {
            System.out.println("begin task");
            Thread.sleep(3000);
            String data1 = "長時間處理任務(wù)后從遠(yuǎn)程返回的值1 threadName=" + Thread.currentThread().getName();
            String data2 = "長時間處理任務(wù)后從遠(yuǎn)程返回的值2 threadName=" + Thread.currentThread().getName();
            synchronized (this){
                getData1 = data1;
                getData2 = data2;
            }
            System.out.println(getData1);
            System.out.println(getData2);
            System.out.println("end task");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

輸出如下:

begin task
begin task
長時間處理任務(wù)后從遠(yuǎn)程返回的值1 threadName=B
長時間處理任務(wù)后從遠(yuǎn)程返回的值2 threadName=A
end task
長時間處理任務(wù)后從遠(yuǎn)程返回的值1 threadName=A
長時間處理任務(wù)后從遠(yuǎn)程返回的值2 threadName=A
end task
耗時:3s

從上面代碼可以看出當(dāng)一個線程訪問一個對象的synchronized同步代碼塊時庄吼,另一個線程任然可以訪問該對象非synchronized同步代碼塊缎除。不在synchronized塊中的就是異步執(zhí)行,在synchronized塊中就是同步執(zhí)行总寻。

synchronized代碼塊之間的同步性

當(dāng)一個線程訪問一個對象的synchronized(this)同步代碼塊時器罐,其他線程對同一個object中的其他synchronized(this)同步代碼塊訪問將被阻塞。

如果在一個類中有很多個synchronized方法渐行,這是雖然能實(shí)現(xiàn)同步轰坊,但會受到阻塞。如果使用同步代碼塊鎖非this對象祟印,則synchronized(非this)代碼塊中的程序與同步方法是異步的肴沫,不與其他this同步方法爭搶this鎖。

靜態(tài)同步synchronized方法與synchronized(class)代碼塊

關(guān)鍵字synchronized還可以在static方法是使用蕴忆,是對當(dāng)前的*.java文件的Class類進(jìn)行加鎖颤芬。非靜態(tài)的synchronized關(guān)鍵字是給對象加鎖。

public static void printA() {
        synchronized (Service.class) {
            try {
                System.out.println(
                        "線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進(jìn)入printA");
                Thread.sleep(3000);
                System.out.println(
                        "線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開printA");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    synchronized public static void printB() {
        System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進(jìn)入printB");
        System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開printB");
    }

    synchronized public void printC() {
        System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "進(jìn)入printC");
        System.out.println("線程名稱為:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "離開printC");
    }
public class Main {

    public static void main(String[] args){
        Service service = new Service();
        new Thread(Service::printA, "A").start();
        new Thread(Service::printB, "B").start();
        new Thread(() -> service.printC(), "C").start();


    }
}

輸出內(nèi)容:

線程名稱為:A在1552262297299進(jìn)入printA
線程名稱為:C在1552262297300進(jìn)入printC
線程名稱為:C在1552262297300離開printC
線程名稱為:A在1552262300301離開printA
線程名稱為:B在1552262300301進(jìn)入printB
線程名稱為:B在1552262300301離開printB

從運(yùn)行結(jié)果可以看出:靜態(tài)同步synchronized方法與synchronized(class)代碼塊持有的鎖一樣孽文,都是Class鎖,Class鎖對對象的所有實(shí)例起作用夺艰。synchronized關(guān)鍵字加到非static靜態(tài)方法上持有的是對象鎖芋哭。線程A,B和線程C持有的鎖不一樣,所以A和B運(yùn)行同步郁副,但是和C運(yùn)行不同步减牺。

數(shù)據(jù)類型String的常量池特性

JVM具有String常量池緩存的功能,將synchronized(string)與String聯(lián)合使用時會出現(xiàn)一些問題存谎。

 String s1 = "a";
    String s2="a";
    System.out.println(s1==s2);//true

比如兩個同步方法都是synchronized("abc"){}那么多線程會持有相同的鎖拔疚,所以大多數(shù)同步代碼塊不用String作為鎖。

本文代碼:GitHub


歡迎關(guān)注公眾號:


公眾號微信
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末既荚,一起剝皮案震驚了整個濱河市稚失,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恰聘,老刑警劉巖句各,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異晴叨,居然都是意外死亡凿宾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門兼蕊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來初厚,“玉大人,你說我怎么就攤上這事孙技〔蹋” “怎么了排作?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長下愈。 經(jīng)常有香客問我纽绍,道長,這世上最難降的妖魔是什么势似? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任拌夏,我火速辦了婚禮,結(jié)果婚禮上履因,老公的妹妹穿的比我還像新娘障簿。我一直安慰自己,他們只是感情好栅迄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布站故。 她就那樣靜靜地躺著,像睡著了一般毅舆。 火紅的嫁衣襯著肌膚如雪西篓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天憋活,我揣著相機(jī)與錄音岂津,去河邊找鬼。 笑死悦即,一個胖子當(dāng)著我的面吹牛吮成,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辜梳,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼粱甫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了作瞄?” 一聲冷哼從身側(cè)響起茶宵,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宗挥,沒想到半個月后节预,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡属韧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年安拟,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宵喂。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡糠赦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拙泽,我是刑警寧澤淌山,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站顾瞻,受9級特大地震影響泼疑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜荷荤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一退渗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蕴纳,春花似錦会油、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至稻薇,卻和暖如春嫂冻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背塞椎。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工桨仿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人忱屑。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓蹬敲,卻偏偏與公主長得像暇昂,于是被迫代替她去往敵國和親莺戒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容