同步鎖
Monitor
- monitor指與synchronized關聯的同步資源所關聯的鎖
- monitor有一個計數器,初始化爲0,如果monitor的計數器爲0,則意味着該monitor的lock還沒有被獲得,某個線程獲得之後將立即對該計數器加一,從此該線程就是這個monitor的所有者了
- 如果一個已經擁有該monitor所有權的線程重入(重新調用該資源),monitor的計數器會再次累加
- 如果monitor已經被其他線程所擁有,則其他線程嘗試獲取該monitor的所有權時,會被陷入阻塞狀態知道monitor計數器變爲0,才能再次嘗試獲取對monitor的擁有權
使用synchronized需要注意的問題
-
與monitor關聯的對象不能爲空
private final Object mutex=null; public void synMethod(){ synchronized(mutex){ //TODO } }
-
synchronized關鍵字作用域不應該太大
public static class Task implements Runnable{ public synchronized void run(){ //TODO } }
上面的代碼對整個線程的執行域也就是run方法都進行了synchronized同步,從而喪失了併發的能力,synchronized關鍵字應該儘可能的只作用於共享資源的讀寫作用域
-
不同的minitor企圖鎖相同的方法
//模擬排隊取號 package online.hengtian.Thread; import java.util.Arrays; import java.util.List; class Run implements Runnable{ private int index=1; private final static int MAX=500; private final static Object obj=new Object(); @Override public void run() { synchronized (obj) { while (index <= MAX) { System.out.println(Thread.currentThread() + " 的號碼是:" + index++); } } } } public class TicketWindow { public static void main(String[] args){ List<String> windows= Arrays.asList( "一號窗口","二號窗口","三號窗口","四號窗口" ); /*****************錯誤示例開始*********************************/ windows.stream() .map(t->new Thread(new Run(),t)) .forEach(Thread::start); /*****************錯誤示例結束*********************************/ windows= Arrays.asList( "一號窗口","二號窗口","三號窗口","四號窗口" ); Runnable r=new Run(); windows.stream() .map(t->new Thread(r,t)) .forEach(Thread::start); } }
在上面所示的錯誤示例中,構造了四個Runnable實例,從而讓每個線程都擁有了自己的monitor,起不到互斥的作用,在錯誤實例的下面也給出了正確的做法
-
多個鎖交叉導致死鎖
private final Object READ=new Object(); private final Object WRITE=new Object(); public void read(){ synchronized(READ){ synchronized(WRITED){ //TDDO } } } public void write(){ synchronized(WRITE){ synchronized(READ){ //TDDO } } }
This Monitor和Class Monitor的詳細介紹
This Monitor
synchronized關鍵字修飾的同一個實例對象的不同方法時,線程爭搶的是同一個Monitor的lock,也就是this的Monitor,通過代碼二可以驗證
//代碼一
package online.hengtian.Thread;
import java.util.concurrent.TimeUnit;
public class MonitorDemo {
public synchronized void Method1(){
System.out.println("Now here is method1");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method1 Termined");
}
public synchronized void Method2(){
System.out.println("Now here is method2");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method2 Termined");
}
public static void main(String[] args){
MonitorDemo demo=new MonitorDemo();
new Thread(demo::Method1,"t1").start();
new Thread(demo::Method2,"t2").start();
}
}
//代碼二
package online.hengtian.Thread;
import java.util.concurrent.TimeUnit;
public class MonitorDemo {
public synchronized void Method1(){
System.out.println("Now here is method1");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method1 Termined");
}
public void Method2(){
synchronized (this) {
System.out.println("Now here is method2");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method2 Termined");
}
}
public static void main(String[] args){
MonitorDemo demo=new MonitorDemo();
new Thread(demo::Method1,"t1").start();
new Thread(demo::Method2,"t2").start();
}
}
代碼一是採用的同步方法的方式,代碼而是用this的monitor,運行後效果一樣,都是運行完方法一之後再運行方法二
Class Monitor
//代碼一
package online.hengtian.Thread;
import java.util.concurrent.TimeUnit;
public class ClassMonitor {
public static synchronized void Method1(){
System.out.println("Now here is method1");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method1 Termined");
}
public static synchronized void Method2(){
System.out.println("Now here is method2");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method2 Termined");
}
public static void main(String[] args){
new Thread(ClassMonitor::Method1,"t1").start();
new Thread(ClassMonitor::Method2,"t2").start();
}
}
//代碼二
package online.hengtian.Thread;
import java.util.concurrent.TimeUnit;
public class ClassMonitor {
public static synchronized void Method1(){
System.out.println("Now here is method1");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method1 Termined");
}
public static void Method2(){
synchronized(ClassMonitor.class){
System.out.println("Now here is method2");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method2 Termined");
}
}
public static void main(String[] args){
new Thread(ClassMonitor::Method1,"t1").start();
new Thread(ClassMonitor::Method2,"t2").start();
}
}
代碼一和二的運行結果一致,都是先運行方法一之後執行方法二,從而可以說明,在同一個類中的靜態方法同步化時,競爭的是class的monitor