你用或者不用:线程中synchronized关键字使用总结
感謝張龍老師!
Synchronized關(guān)鍵字,同步的, synchronized關(guān)鍵字可以修飾方法,還有靜態(tài)代碼塊(static block)
Java中的每一個(gè)對(duì)象都有一把鎖(lock),只是平時(shí)狀態(tài)下,鎖是開著的,當(dāng)被synchronized關(guān)鍵字所修飾時(shí),就將這把鎖激活,當(dāng)一個(gè)對(duì)象被多個(gè)線程所調(diào)用時(shí),由于線程的行進(jìn)度的不確定性,可能會(huì)引發(fā)方法內(nèi)很多問題,而synchronized關(guān)鍵字就是只允許方法每一次只能允許一個(gè)線程訪問,只有當(dāng)線程訪問結(jié)束,或者拋出異常時(shí),才允許下一個(gè)線程的介入:
(張龍老師的文檔說的很清楚了,這點(diǎn)也是非常重要的)
模擬銀行取錢系統(tǒng):
/*
?* 在這個(gè)程序中我遇到了一個(gè)問題
?* 就是為什么要在類中定義一個(gè)其他類的成員變量
?* 我一開始是new出來的,發(fā)現(xiàn)這樣做很不應(yīng)該
?* 特別是在線程中,這樣做是一點(diǎn)都體現(xiàn)不了線程的作用;
?* 這樣做的話,每一次調(diào)用啟用類都會(huì)生成調(diào)用類的對(duì)象;相當(dāng)于是線程之間不會(huì)影響;
?* 而定義成員類,不管生成多少個(gè)啟用類,都可以只傳入一個(gè)調(diào)用類;
?*/
?
package thread;
?
publicclass FetchMoneyTest
{
?? publicstaticvoid main(String[] args)
?? {
????? Bank bank = new Bank();
????? ThreadDemo test1 = new ThreadDemo(bank);
????? ThreadDemo test2 = new ThreadDemo(bank);
????? test1.start();
????? test2.start();
?? }
}
?
class Bank
{
?? publicintmoney = 1000;
?
?? public?int getMoney(int number)throwsInterruptedException
?? {
????? if (number < 0)
????? {
??????? return -1;
?
????? }
????? elseif (number > 1000)
????? {
??????? return -2;
????? }
????? else
????? {
??????? Thread.sleep(1200);
??????? money -= number;
??????? System.out.println(money);
???????
??????? return number;
????? }
?
?? }
?
}
?
class ThreadDemoextends Thread
{
?? private Bankbank;
?? public ThreadDemo(Bank bank)
?? {
????? this.bank = bank;
?? }
?? @Override
?? publicvoid run()
?? {
?????
????? try
????? {
??????? System.out.println(bank.getMoney(800));
????? }
????? catch (InterruptedException e)
????? {
??????? // TODO Auto-generatedcatch block
??????? e.printStackTrace();
????? }
?? }
}
?
下面的程序只改變一點(diǎn),就是只加上synchronized關(guān)鍵字:
/*
?* 在這個(gè)程序中我遇到了一個(gè)問題
?* 就是為什么要在類中定義一個(gè)其他類的成員變量
?* 我一開始是new出來的,發(fā)現(xiàn)這樣做很不應(yīng)該
?* 特別是在線程中,這樣做是一點(diǎn)都體現(xiàn)不了線程的作用;
?* 這樣做的話,每一次調(diào)用啟用類都會(huì)生成調(diào)用類的對(duì)象;相當(dāng)于是線程之間不會(huì)影響;
?* 而定義成員類,不管生成多少個(gè)啟用類,都可以只傳入一個(gè)調(diào)用類;
?*/
?
package thread;
?
publicclass FetchMoneyTest
{
?? publicstaticvoid main(String[] args)
?? {
????? Bank bank = new Bank();
????? ThreadDemo test1 = new ThreadDemo(bank);
????? ThreadDemo test2 = new ThreadDemo(bank);
????? test1.start();
????? test2.start();
?? }
}
?
class Bank
{
?? publicintmoney = 1000;
?
?? publicsynchronizedint getMoney(int number)throws InterruptedException
?? //加上了synchronized關(guān)鍵字后,線程將會(huì)卓一的通過這個(gè)方法,第二次的時(shí)候顯然不符合方法的第三個(gè)約定,返回-3!
?? {
????? if (number < 0)
????? {
??????? return -1;
?
????? }
????? elseif (number > 1000)
????? {
??????? return -2;
????? }
????? elseif(money < number)
????? {
??????? return -3;
????? }
????? else
????? {
??????? Thread.sleep(1200);
??????? money -= number;
??????? System.out.println(money);
???????
??????? return number;
????? }
?
?? }
?
}
?
class ThreadDemoextends Thread
{
?? private Bankbank;
?? public ThreadDemo(Bank bank)
?? {
????? this.bank = bank;
?? }
?? @Override
?? publicvoid run()
?? {
?????
????? try
????? {
??????? System.out.println(bank.getMoney(800));
????? }
????? catch (InterruptedException e)
????? {
??????? // TODO Auto-generatedcatch block
??????? e.printStackTrace();
????? }
?? }
}
?
??
25.
人是不能夠懶得,我應(yīng)該有自己的一套,下面來深度的理解張龍老師說的一句話,被synchronized修飾方法的對(duì)象會(huì)上鎖,如果一個(gè)類的多個(gè)方法都被synchronized修飾時(shí),當(dāng)一個(gè)線程未訪問完畢其中的一個(gè)synchronized方法時(shí),其他的線程是不能訪問任何該對(duì)象的任何一個(gè)synchronized方法的,除非你生成了兩個(gè)對(duì)象!
下面我們來一步步的分析:
首先 當(dāng)一個(gè)類中的兩個(gè)方法都沒有被synchronized修飾時(shí),這兩個(gè)方法的打印將是隨機(jī)進(jìn)行的:
package thread;
?
publicclass ThreadTest10
{
?? publicstaticvoid main(String[] args)
?? {
????? Demo demo1 = new Demo();
????? ThreadTest11 test1 = new ThreadTest11(demo1);
????? ThreadTest12 test2 = new ThreadTest12(demo1);
????? test1.start();
????? test2.start();
?? }
}
class Demo
{
?? publicvoid method1()throws InterruptedException
?? {
????? for(int i = 0 ; i < 15 ; i ++)
????? {
??????? Thread.sleep((long)(Math.random()*1000));
??????? System.out.println("hello:"+ i );
????? }
?? }
?? publicvoid method2()throws InterruptedException
?? {
?????
????? for(int i = 0 ; i < 15; i ++)
????? {
??????? Thread.sleep((long)(Math.random()*1000));
??????? System.out.println("world:"+i);
???????
????? }
?? }
}
class ThreadTest11extends Thread
{
?? private Demodemo;
?? public ThreadTest11 (Demo demo)
?? {
????? this.demo = demo;
?? }
?? @Override
?? publicvoid run()
?? {
????? try
????? {
??????? demo.method1();
????? }
????? catch (InterruptedException e)
????? {
??????? // TODO Auto-generatedcatch block
??????? e.printStackTrace();
????? }
?? }
??
??
}
class ThreadTest12extends Thread
{
?? private Demodemo;
?? public ThreadTest12 (Demo demo)
?? {
????? this.demo = demo;
?? }
?? @Override
?? publicvoid run()
?? {
????? try
????? {
??????? demo.method2();
????? }
????? catch (InterruptedException e)
????? {
??????? // TODO Auto-generatedcatch block
?? ????? e.printStackTrace();
????? }
?? }
??
??
}
?
?
?
?
現(xiàn)在我要為上面的兩個(gè)方法加上synchronized關(guān)鍵字,使之變得有序,如果只加上一個(gè)的話,是沒有意義的,還是亂序,因?yàn)橥椒椒?#xff0c;可以和非同步方法同時(shí)運(yùn)行;
加上后將會(huì)輸出有序;先輸出hello 或者 world;
?
?
下面
說過 synchronized只修飾一個(gè)對(duì)象,如果我傳入的是兩個(gè)對(duì)象,即使加上了synchronized關(guān)鍵字,它們也不會(huì)互相影響;
更改如下:
publicstaticvoid main(String[] args)
?? {
????? Demodemo1 = new Demo();
????? ThreadTest11 test1 = new ThreadTest11(demo1);
????? demo1 = newDemo();
????? ThreadTest12 test2 = new ThreadTest12(demo1);
????? test1.start();
????? test2.start();
?? }
?
?
下面將是講述的重點(diǎn),就是關(guān)鍵字synchronized 與static合用的效果,我們知道,不管一個(gè)類生成多少個(gè)對(duì)象,這些對(duì)象實(shí)例都只有一個(gè)共同的類,synchronized只能鎖住一個(gè)類的對(duì)象,而被 synchronized和static修飾的方法,將會(huì)鎖住對(duì)象的類!!,即不管你生成多少個(gè)類的對(duì)象,我們都相當(dāng)于你調(diào)用的是同一個(gè)對(duì)象;所以上面的輸出將會(huì)變得有序!
package thread;
?
publicclass ThreadTest10
{
?? publicstaticvoid main(String[] args)
?? {
????? Demo demo1 = new Demo();
????? ThreadTest11 test1 = new ThreadTest11(demo1);
????? demo1 = new Demo();
????? ThreadTest12 test2 = new ThreadTest12(demo1);
????? test1.start();
????? test2.start();
?? }
}
class Demo
{
?? publicsynchronizedstaticvoid method1()throws InterruptedException
?? {
????? for(int i = 0 ; i < 15 ; i ++)
????? {
??????? Thread.sleep((long)(Math.random()*1000));
??????? System.out.println("hello:"+ i );
????? }
?? }
?? publicsynchronizedstaticvoid method2()throwsInterruptedException
?? {
?????
????? for(int i = 0 ; i < 15; i ++)
????? {
??????? Thread.sleep((long)(Math.random()*1000));
??????? System.out.println("world:"+i);
???????
????? }
?? }
}
class ThreadTest11extends Thread
{
?? private Demodemo;
?? public ThreadTest11 (Demo demo)
?? {
????? this.demo = demo;
?? }
?? @Override
?? publicvoid run()
?? {
????? try
????? {
??????? demo.method1();
????? }
????? catch (InterruptedException e)
????? {
??????? // TODO Auto-generatedcatch block
??????? e.printStackTrace();
????? }
?? }
??
??
}
class ThreadTest12extends Thread
{
?? private Demodemo;
?? public ThreadTest12 (Demo demo)
?? {
????? this.demo = demo;
?? }
?? @Override
?? publicvoid run()
?? {
????? try
????? {
??????? demo.method2();
????? }
????? catch (InterruptedException e)
????? {
??????? // TODO Auto-generatedcatch block
??????? e.printStackTrace();
????? }
?? }
??
}
?
?
?
?
26.
下面介紹synchronized代碼塊,個(gè)人以為,重要度遠(yuǎn)遠(yuǎn)高于單純的修飾synchronized方法:
一方面:高效率!
另一方面:后面涉及到的wait和notify方法,都要涉及
Synchronized要修飾一個(gè)對(duì)象
即:synchronized(Object);
表示將對(duì)象object上鎖,這里的object對(duì)象其實(shí)是沒有用的,只是說明被上鎖,個(gè)人建議使用this關(guān)鍵字,表示將當(dāng)前對(duì)象上鎖!
看下面的synchronized代碼塊代碼同樣實(shí)現(xiàn)了兩個(gè)方法的順序執(zhí)行:
?
?
?
package thread;
?
publicclass BlockTest
{
?? publicstaticvoid main(String[] args)
?? {
????? Block block = new Block();
????? ThreadTestX test1 = new ThreadTestX(block);
????? ThreadTestXX test2 = new ThreadTestXX(block);
????? test1.start();
????? test2.start();
?? }
}
class Block
{
?? publicvoid method1()
?? {
????? synchronized (this)
????? {
??????? for(int i = 0; i < 15; i++)
??????? {
?????????? System.out.println("hello :"+ i);
??????????
??????? ??
??????? }
????? }
?? }
?? publicvoid method2()
?? {
????? synchronized(this)
????? {
??????? for(int i = 0 ; i <15; i ++)
??????? {
?????????? System.out.println("world!"+ i);
??????????
??????? }
????? }
?? }
}
class ThreadTestXextends Thread
{
?? private Blockblock;
?? public ThreadTestX(Block block)
?? {
????? this.block = block;
?? }
?? @Override
?? publicvoid run()
?? {
????? block.method1();
?? }
??
}
class ThreadTestXXextends Thread
{
?? private Blockblock;
?? public ThreadTestXX(Block block)
?? {
????? this.block = block;
?? }
?? @Override
?? publicvoid run()
?? {
????? block.method2();
?? }
??
}
?
?
總結(jié)
以上是生活随笔為你收集整理的你用或者不用:线程中synchronized关键字使用总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于可变字符串StringBuffer和
- 下一篇: Object类中的两个方法——wait和