生活随笔
收集整理的這篇文章主要介紹了
0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
什么是同步
- 在上一篇0036 Java學習筆記-多線程-創建線程的三種方式示例代碼中,實現Runnable創建多條線程,輸出中的結果中會有錯誤,比如一張票賣了兩次,有的票沒賣的情況,因為線程對象被多條線程訪問,一條線程在執行一個循環的過程中被中斷,下一個線程則出現錯誤
- 因此,線程任務中可能引起錯誤的地方應當被一次執行完畢
同步代碼塊
package testpack;public class Test1 { public static void main(String[] args){ System.out.println("現在是主線程: "+Thread.currentThread()); System.out.println("下面新建兩個線程");A a=new A(500); new Thread(a,"線程A").start(); new Thread(a,"線程B").start();new Thread(a,"線程C").start();}
}
class A implements Runnable{private int tickets;A (int tick){tickets=tick;}private Object obj=new Object(); //同步監視器public void run() {synchronized(obj){ //同步代碼塊for (;tickets>0;tickets--) {System.out.println("當前線程:"+Thread.currentThread()+" 賣出第 "+tickets+" 張票。");try{Thread.sleep(1); //讓當前線程暫停1毫秒,其他線程也不能執行該同步代碼塊}catch(InterruptedException ex){ex.printStackTrace();}if (tickets==1){System.out.println("票已賣完,當前線程是: "+Thread.currentThread());}}}}
}
- 同步監視器,就是一個普通的對象,就像一把鎖,只有獲得了同步監視器的線程才能執行同步代碼塊。
- 同步代碼塊執行一次完畢后,將會釋放鎖,接下來是這條線程拿到同步鎖,還是其他其他線程,則不一定,根據線程調度而定,但是在同步代碼塊執行過程中,不會被中斷,一個同步任務會被一次執行完畢
同步方法
- 在同步代碼塊中,synchonized關鍵字在run()方法內部,修飾的是一段代碼,也可以用來修飾run()方法,也就是同步方法
- synchronized不只可以修飾run()方法,還可以修飾其他方法,只要是需要一次同步完成的任務,然后再在run()方法中被調用
- 同步方法中有一個隱式的同步監視器,就是this,也就是調用run()方法(或同步方法)的這個對象
- 還是上面的實例,用同步方法改寫
package testpack;public class Test1 { public static void main(String[] args){ System.out.println("現在是主線程: "+Thread.currentThread()); System.out.println("下面新建兩個線程");A a=new A(500);new Thread(a,"線程A").start();new Thread(a,"線程B").start();new Thread(a,"線程C").start();}
}
class A implements Runnable{private int tickets;A (int tick){tickets=tick;}public synchronized void run() { //同步方法for (;tickets>0;tickets--) {System.out.println("當前線程:"+Thread.currentThread()+" 賣出第 "+tickets+" 張票。");try{Thread.sleep(1);}catch(InterruptedException ex){ex.printStackTrace();}if (tickets==1){System.out.println("票已賣完,當前線程是: "+Thread.currentThread());}}}
}
釋放同步監視器
- 當前線程的同步任務(同步方法、同步代碼塊)執行完畢
- 在同步任務中,遇到break、return,終止了同步任務
- 在同步任務中,出現Error、Exception等,導致同步任務結束
- 在同步任務中,執行了同步監視器對象的wait()方法,則當前線程暫停,并釋放同步監視器
- 不會釋放同步監視器的情況:
- 同步任務中,調用Thread.sleep()、Thread.yield()方法來暫停當前線程的執行
- 同步任務中,其他線程調用了該線程的suspend()方法將該線程掛起
同步鎖
- 除了可以用new Object()和this作同步監視器往外,還可以定義專門的同步鎖,且功能更加強
- Lock接口
- ReadWriteLock
- ReentrantReadWriteLock實現類
- ReentrantReadWriteLock.ReadLock
- ReentrantReadWriteLock.WriteLock
- StampedLock
- 示例:用ReentrantLock改寫上面的代碼
package testpack;import java.util.concurrent.locks.ReentrantLock;public class Test1 { public static void main(String[] args){ System.out.println("現在是主線程: "+Thread.currentThread()); System.out.println("下面新建兩個線程");A a=new A(50);new Thread(a,"線程A").start();new Thread(a,"線程B").start();new Thread(a,"線程C").start();}
}
class A implements Runnable{private final ReentrantLock lock=new ReentrantLock(); //定義一個同步鎖private int tickets;A (int tick){tickets=tick;}public void run() {lock.lock(); //加鎖for (;tickets>0;tickets--) {System.out.println("當前線程:"+Thread.currentThread()+" 賣出第 "+tickets+" 張票。");if (tickets==1){System.out.println("票已賣完,當前線程是: "+Thread.currentThread());}}lock.unlock(); //釋放鎖}
}
死鎖
- 兩個線程各拿一把鎖,下一步運行都需要對方手里那把鎖,但都拿不到,則造成死鎖,程序不能繼續執行
package testpack;
public class Test1 { public static void main(String[] args){ DeadLock dl=new DeadLock();new Thread(dl).start();dl.init();}
}
class DeadLock implements Runnable {A a=new A();B b=new B();public void init(){a.a1(b);System.out.println("進入主線程");}public void run(){b.b1(a);System.out.println("進入子線程");}
}
class A {public synchronized void a1(B b){System.out.println("當前線程是:"+Thread.currentThread().getName()+" ,正在執行a1()");try{Thread.sleep(10);}catch(InterruptedException ex){ex.printStackTrace();}System.out.println("當前線程是:"+Thread.currentThread().getName()+" ,準備調用b2()");b.b2(); //b2方法是同步方法,調用該方法要對調用的對象b加鎖}public synchronized void a2(){System.out.println("這是a2()方法");}
}
class B{public synchronized void b1(A a){System.out.println("當前線程是:"+Thread.currentThread().getName()+" ,正在執行b1()");try{Thread.sleep(10);}catch(InterruptedException ex){ex.printStackTrace();}System.out.println("當前線程是:"+Thread.currentThread().getName()+" ,準備調用a2()");a.a2(); //a2方法是同步方法,調用該方法要對調用的對象a加鎖}public synchronized void b2(){System.out.println("這是b2()方法");}
}
- 上面在調用a.a2()和b.b2()方法時,分別要對a對象和b對象加鎖,但這時,a、b對象的鎖都在對方手里,造成兩個線程阻塞
其他
- 可變類的線程安全是以降低程序的運行效率為代價的
- 不要對線程安全類的所有方法都進行同步,只對那些改變共享資源的方法進行同步
- 如果一個類有單線程和多線程運行環境,那么應該提供兩種版本,就是StringBuilder(單線程)和StringBuffer(多線程)一樣
轉載于:https://www.cnblogs.com/sonng/p/6134444.html
總結
以上是生活随笔為你收集整理的0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。