多執行緒安全
髒讀:多個執行緒對同一個物件的例項變數進行修改後訪問,導致讀到的資料是被修改過的。
例項
ThreadDomain16類
public class ThreadDomain16 {
private int num = 0;
public void addNum(String userName)
{
try
{
if ("a".equals(userName))
{
num = 100;
System.out.println("a set over!");
Thread.sleep(2000);
}
else
{
num = 200;
System.out.println("b set over!");
}
System.out.println(userName + " num = " + num);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
MyThread16_0類
public class MyThread16_0 extends Thread{
private ThreadDomain16 td;
public MyThread16_0(ThreadDomain16 td)
{
this.td = td;
}
public void run()
{
td.addNum("a");
}
}
MyThread16_1類
啟動MyThread16_0 和MyThread16_1 執行緒
public class MyThread16_1 extends Thread {
private ThreadDomain16 td;
public MyThread16_1(ThreadDomain16 td)
{
this.td = td;
}
public void run()
{
td.addNum("b");
}
}
main方法
public class MyThread16_main_0 {
public static void main(String[] args)
{
ThreadDomain16 td = new ThreadDomain16();
MyThread16_0 mt0 = new MyThread16_0(td);
MyThread16_1 mt1 = new MyThread16_1(td);
mt0.start();
mt1.start();
}
}
輸出結果如下
a set over!
b set over!
b num = 200
a num = 200
理論上應該輸出a num = 100,b num = 200,結果a num = 200。
我們來分析下執行流程,MyThread16_0執行緒修改num為100,此時sleep兩秒,在MyThread16_0 sleep的過程中,MyThread16_1執行緒修改num為200。導致MyThread16_0的num也變成200。最後先輸出b num = 200,再輸出a num = 200。出現了髒讀。
解決該問題很簡單,我們在addNum()前面加上synchronized關鍵字
public synchronized void addNum(String userName)
{
try
{
if ("a".equals(userName))
{
num = 100;
System.out.println("a set over!");
Thread.sleep(2000);
}
else
{
num = 200;
System.out.println("b set over!");
}
System.out.println(userName + " num = " + num);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
輸出結果如下
a set over!
a num = 100
b set over!
b num = 200
按序列印,synchronized修飾的方法形成了鎖,哪個執行緒先執行了synchronized修飾的方法哪個執行緒就先獲得了鎖,其他執行緒必須等待,直到該執行緒釋放鎖,等待的執行緒獲得鎖方可執行。
一個物件一個鎖
同步情況
下,使用如下main方法
public class MyThread16_main_1 {
public static void main(String[] args)
{
ThreadDomain16 td0 = new ThreadDomain16();
ThreadDomain16 td1 = new ThreadDomain16();
MyThread16_0 mt0 = new MyThread16_0(td0);
MyThread16_1 mt1 = new MyThread16_1(td1);
mt0.start();
mt1.start();
}
}
執行結果
a set over!
b set over!
b num = 200
a num = 100
出現了交叉列印,b沒有等待a執行結束,說明synchronized是物件鎖,而不是方法鎖。這裡宣告瞭兩個ThreadDomain16物件,所以有兩個物件鎖,不受“等待鎖的釋放”的制約。
那麼我們把addNum方法前面的synchronized關鍵字去掉,非同步情況
下
執行結果如下
a set over!
b set over!
b num = 200
a num = 100
可以看到,沒有synchronized的幫助,依然避免了髒讀現象。例項變數num為每個物件獨自佔有。但是,這種解決方式需要宣告多個物件,不推薦。
synchronized方法與鎖物件
現在看一個例子,一個方法有synchronized修飾,另一個方法無synchronized修飾
ThreadDomain17類
public class ThreadDomain17 {
public synchronized void methodA()
{
try
{
System.out.println("開始 methodA, 執行緒名稱 = " +
Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("結束 methodA, 執行緒名稱 = " +
Thread.currentThread().getName());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public void methodB()
{
try
{
System.out.println("開始 methodB, 執行緒名稱 = " +
Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("結束 methodB, 執行緒名稱 = " +
Thread.currentThread().getName());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
MyThread17_0類
public class MyThread17_0 extends Thread{
private ThreadDomain17 td;
public MyThread17_0(ThreadDomain17 td)
{
this.td = td;
}
public void run()
{
td.methodA();
}
}
MyThread17_1類
public class MyThread17_1 extends Thread{
private ThreadDomain17 td;
public MyThread17_1(ThreadDomain17 td)
{
this.td = td;
}
public void run()
{
td.methodB();
}
}
main方法
public class MyThread17_main {
public static void main(String[] args) {
ThreadDomain17 td = new ThreadDomain17();
MyThread17_0 mt0 = new MyThread17_0(td);
mt0.setName("A");
MyThread17_1 mt1 = new MyThread17_1(td);
mt1.setName("B");
mt0.start();
mt1.start();
}
}
執行結果如下
開始 methodB, 執行緒名稱 = B
開始 methodA, 執行緒名稱 = A
結束 methodA, 執行緒名稱 = A
結束 methodB, 執行緒名稱 = B
可以看到,非synchronized的methodB方法以非同步方式被呼叫。
把MyThread17_1類的run方法改成td.methodA();
輸出結果如下
開始 methodA, 執行緒名稱 = A
結束 methodA, 執行緒名稱 = A
開始 methodA, 執行緒名稱 = B
結束 methodA, 執行緒名稱 = B
待執行的執行緒在等待synchronized方法同步再執行。
A執行緒持有ThreadDomain17物件鎖,B執行緒可以非同步方式呼叫ThreadDomain17物件的非synchronized方法;
A執行緒持有ThreadDomain17物件鎖,B執行緒呼叫ThreadDomain17物件的synchronized方法需要等待;
把MyThread17_1類的run方法改回td.methodB();把ThreadDomain17類的methodB方法加上synchronized關鍵字,輸出結果如下
開始 methodA, 執行緒名稱 = A
結束 methodA, 執行緒名稱 = A
開始 methodB, 執行緒名稱 = B
結束 methodB, 執行緒名稱 = B
這個很好理解,先執行的執行緒獲得同步鎖,等該執行緒必須執行完畢,等待的執行緒才能執行,所有的操作都是同步的。
synchronized鎖重入
synchronized是可重入鎖,當一個執行緒得到物件鎖之後,可以再次申請物件鎖並獲得物件鎖。
ThreadDomain18類
public class ThreadDomain18 {
public synchronized void print1()
{
System.out.println("ThreadDomain18.print1()");
print2();
}
public synchronized void print2()
{
System.out.println("ThreadDomain18.print2()");
print3();
}
public synchronized void print3()
{
System.out.println("ThreadDomain18.print3()");
}
}
MyThread18類
public class MyThread18 extends Thread{
public void run()
{
ThreadDomain18 td = new ThreadDomain18();
td.print1();
}
public static void main(String[] args)
{
MyThread18 mt = new MyThread18();
mt.start();
}
}
結果如下
ThreadDomain18.print1()
ThreadDomain18.print2()
ThreadDomain18.print3()
結果說明,MyThread18在有鎖的情況下多次獲取到物件鎖
異常釋放鎖
ThreadDomain19類
public class ThreadDomain19 {
public synchronized void testMethod()
{
try
{
System.out.println("進入 ThreadDomain19.testMethod(), currentThread = " +
Thread.currentThread().getName());
long l = 5;
while (true)
{
long lo = 2 / l;
Thread.sleep(1000);
l--;
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
MyThread19類
public class MyThread19 extends Thread {
private ThreadDomain19 td;
public MyThread19(ThreadDomain19 td)
{
this.td = td;
}
public void run()
{
td.testMethod();
}
public static void main(String[] args)
{
ThreadDomain19 td = new ThreadDomain19();
MyThread19 mt0 = new MyThread19(td);
MyThread19 mt1 = new MyThread19(td);
mt0.start();
mt1.start();
}
}
輸出結果如下
進入 ThreadDomain19.testMethod(), currentThread = Thread-1
java.lang.ArithmeticException: / by zero
進入 ThreadDomain19.testMethod(), currentThread = Thread-0
at com.advance.MultiThread3.ThreadDomain19.testMethod(ThreadDomain19.java:18)
at com.advance.MultiThread3.MyThread19.run(MyThread19.java:18)
java.lang.ArithmeticException: / by zero
at com.advance.MultiThread3.ThreadDomain19.testMethod(ThreadDomain19.java:18)
at com.advance.MultiThread3.MyThread19.run(MyThread19.java:18)
由上可見,一旦出現異常,鎖自動釋放。Thread-1剛拋完異常,此時Thread-1釋放了鎖,Thread-0就開始執行。
synchronized方法塊
ThreadDomain20類
public class ThreadDomain20 extends Thread{
public void doLongTimeTask() throws Exception
{
for (int i = 0; i < 100; i++)
{
System.out.println("nosynchronized threadName = " +
Thread.currentThread().getName() + ", i = " + (i + 1));
}
System.out.println();
synchronized (this){
for (int i = 0; i < 100; i++)
{
System.out.println("synchronized threadName = " +
Thread.currentThread().getName() + ", i = " + (i + 1));
}
}
}
}
MyThread20類
public class MyThread20 extends Thread{
private ThreadDomain20 td;
public MyThread20(ThreadDomain20 td)
{
this.td = td;
}
public void run()
{
try
{
td.doLongTimeTask();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
ThreadDomain20 td = new ThreadDomain20();
MyThread20 mt0 = new MyThread20(td);
mt0.setName("A");
MyThread20 mt1 = new MyThread20(td);
mt1.setName("B");
mt0.start();
mt1.start();
}
}
執行結果如下
nosynchronized threadName = A, i = 1
nosynchronized threadName = B, i = 1
nosynchronized threadName = A, i = 2
nosynchronized threadName = B, i = 2
nosynchronized threadName = A, i = 3
nosynchronized threadName = B, i = 3
nosynchronized threadName = A, i = 4
nosynchronized threadName = B, i = 4
nosynchronized threadName = A, i = 5
nosynchronized threadName = B, i = 5
nosynchronized threadName = A, i = 6
nosynchronized threadName = B, i = 6
nosynchronized threadName = A, i = 7
nosynchronized threadName = B, i = 7
nosynchronized threadName = A, i = 8
nosynchronized threadName = B, i = 8
nosynchronized threadName = A, i = 9
nosynchronized threadName = B, i = 9
nosynchronized threadName = A, i = 10
nosynchronized threadName = B, i = 10
nosynchronized threadName = A, i = 11
nosynchronized threadName = B, i = 11
nosynchronized threadName = A, i = 12
nosynchronized threadName = B, i = 12
nosynchronized threadName = A, i = 13
nosynchronized threadName = B, i = 13
nosynchronized threadName = A, i = 14
nosynchronized threadName = B, i = 14
nosynchronized threadName = A, i = 15
nosynchronized threadName = A, i = 16
nosynchronized threadName = A, i = 17
nosynchronized threadName = A, i = 18
nosynchronized threadName = B, i = 15
nosynchronized threadName = A, i = 19
nosynchronized threadName = B, i = 16
nosynchronized threadName = A, i = 20
nosynchronized threadName = B, i = 17
nosynchronized threadName = A, i = 21
nosynchronized threadName = B, i = 18
nosynchronized threadName = A, i = 22
nosynchronized threadName = B, i = 19
nosynchronized threadName = A, i = 23
nosynchronized threadName = A, i = 24
nosynchronized threadName = B, i = 20
nosynchronized threadName = A, i = 25
nosynchronized threadName = B, i = 21
nosynchronized threadName = A, i = 26
nosynchronized threadName = B, i = 22
nosynchronized threadName = A, i = 27
nosynchronized threadName = B, i = 23
nosynchronized threadName = A, i = 28
nosynchronized threadName = B, i = 24
nosynchronized threadName = A, i = 29
nosynchronized threadName = B, i = 25
nosynchronized threadName = A, i = 30
nosynchronized threadName = B, i = 26
nosynchronized threadName = A, i = 31
nosynchronized threadName = A, i = 32
nosynchronized threadName = B, i = 27
nosynchronized threadName = A, i = 33
nosynchronized threadName = B, i = 28
nosynchronized threadName = A, i = 34
nosynchronized threadName = B, i = 29
nosynchronized threadName = A, i = 35
nosynchronized threadName = B, i = 30
nosynchronized threadName = A, i = 36
nosynchronized threadName = B, i = 31
nosynchronized threadName = A, i = 37
nosynchronized threadName = B, i = 32
nosynchronized threadName = A, i = 38
nosynchronized threadName = A, i = 39
nosynchronized threadName = B, i = 33
nosynchronized threadName = A, i = 40
nosynchronized threadName = B, i = 34
nosynchronized threadName = A, i = 41
nosynchronized threadName = B, i = 35
nosynchronized threadName = A, i = 42
nosynchronized threadName = B, i = 36
nosynchronized threadName = A, i = 43
nosynchronized threadName = B, i = 37
nosynchronized threadName = A, i = 44
nosynchronized threadName = B, i = 38
nosynchronized threadName = A, i = 45
nosynchronized threadName = B, i = 39
nosynchronized threadName = A, i = 46
nosynchronized threadName = B, i = 40
nosynchronized threadName = A, i = 47
nosynchronized threadName = B, i = 41
nosynchronized threadName = A, i = 48
nosynchronized threadName = B, i = 42
nosynchronized threadName = A, i = 49
nosynchronized threadName = B, i = 43
nosynchronized threadName = A, i = 50
nosynchronized threadName = B, i = 44
nosynchronized threadName = B, i = 45
synchronized threadName = A, i = 1
nosynchronized threadName = B, i = 46
synchronized threadName = A, i = 2
nosynchronized threadName = B, i = 47
synchronized threadName = A, i = 3
nosynchronized threadName = B, i = 48
synchronized threadName = A, i = 4
nosynchronized threadName = B, i = 49
nosynchronized threadName = B, i = 50
synchronized threadName = A, i = 5
synchronized threadName = A, i = 6
synchronized threadName = A, i = 7
synchronized threadName = A, i = 8
synchronized threadName = A, i = 9
synchronized threadName = A, i = 10
synchronized threadName = A, i = 11
synchronized threadName = A, i = 12
synchronized threadName = A, i = 13
synchronized threadName = A, i = 14
synchronized threadName = A, i = 15
synchronized threadName = A, i = 16
synchronized threadName = A, i = 17
synchronized threadName = A, i = 18
synchronized threadName = A, i = 19
synchronized threadName = A, i = 20
synchronized threadName = A, i = 21
synchronized threadName = A, i = 22
synchronized threadName = A, i = 23
synchronized threadName = A, i = 24
synchronized threadName = A, i = 25
synchronized threadName = A, i = 26
synchronized threadName = A, i = 27
synchronized threadName = A, i = 28
synchronized threadName = A, i = 29
synchronized threadName = A, i = 30
synchronized threadName = A, i = 31
synchronized threadName = A, i = 32
synchronized threadName = A, i = 33
synchronized threadName = A, i = 34
synchronized threadName = A, i = 35
synchronized threadName = A, i = 36
synchronized threadName = A, i = 37
synchronized threadName = A, i = 38
synchronized threadName = A, i = 39
synchronized threadName = A, i = 40
synchronized threadName = A, i = 41
synchronized threadName = A, i = 42
synchronized threadName = A, i = 43
synchronized threadName = A, i = 44
synchronized threadName = A, i = 45
synchronized threadName = A, i = 46
synchronized threadName = A, i = 47
synchronized threadName = A, i = 48
synchronized threadName = A, i = 49
synchronized threadName = A, i = 50
synchronized threadName = B, i = 1
synchronized threadName = B, i = 2
synchronized threadName = B, i = 3
synchronized threadName = B, i = 4
synchronized threadName = B, i = 5
synchronized threadName = B, i = 6
synchronized threadName = B, i = 7
synchronized threadName = B, i = 8
synchronized threadName = B, i = 9
synchronized threadName = B, i = 10
synchronized threadName = B, i = 11
synchronized threadName = B, i = 12
synchronized threadName = B, i = 13
synchronized threadName = B, i = 14
synchronized threadName = B, i = 15
synchronized threadName = B, i = 16
synchronized threadName = B, i = 17
synchronized threadName = B, i = 18
synchronized threadName = B, i = 19
synchronized threadName = B, i = 20
synchronized threadName = B, i = 21
synchronized threadName = B, i = 22
synchronized threadName = B, i = 23
synchronized threadName = B, i = 24
synchronized threadName = B, i = 25
synchronized threadName = B, i = 26
synchronized threadName = B, i = 27
synchronized threadName = B, i = 28
synchronized threadName = B, i = 29
synchronized threadName = B, i = 30
synchronized threadName = B, i = 31
synchronized threadName = B, i = 32
synchronized threadName = B, i = 33
synchronized threadName = B, i = 34
synchronized threadName = B, i = 35
synchronized threadName = B, i = 36
synchronized threadName = B, i = 37
synchronized threadName = B, i = 38
synchronized threadName = B, i = 39
synchronized threadName = B, i = 40
synchronized threadName = B, i = 41
synchronized threadName = B, i = 42
synchronized threadName = B, i = 43
synchronized threadName = B, i = 44
synchronized threadName = B, i = 45
synchronized threadName = B, i = 46
synchronized threadName = B, i = 47
synchronized threadName = B, i = 48
synchronized threadName = B, i = 49
synchronized threadName = B, i = 50
結論:
A執行緒訪問synchronized方法塊的時候,B執行緒依然可以訪問非synchronized方法塊的內容;
A執行緒訪問synchronized方法塊的時候,B執行緒訪問synchronized方法塊會被阻塞;
synchronized方法塊和synchronized方法
synchronized關鍵字修飾的方法和synchronized方法塊修飾的方法效果一致。synchronized方法塊可以提高程式碼執行效率,在需要同步的地方去同步。
ThreadDomain21類
public class ThreadDomain21 {
public synchronized void otherMethod()
{
System.out.println("----------run--otherMethod");
}
public void doLongTask()
{
synchronized (this)
{
for (int i = 0; i < 100; i++)
{
System.out.println("synchronized threadName = " +
Thread.currentThread().getName() + ", i = " + (i + 1));
try
{
Thread.sleep(5);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
MyThread21_0類
public class MyThread21_0 extends Thread{
private ThreadDomain21 td;
public MyThread21_0(ThreadDomain21 td)
{
this.td = td;
}
public void run()
{
td.doLongTask();
}
}
MyThread21_1類
public class MyThread21_1 extends Thread{
private ThreadDomain21 td;
public MyThread21_1(ThreadDomain21 td)
{
this.td = td;
}
public void run()
{
td.otherMethod();
}
}
main方法,sleep一會,讓mt0先執行。
public class MyThread21_main {
public static void main(String[] args) throws Exception
{
ThreadDomain21 td = new ThreadDomain21();
MyThread21_0 mt0 = new MyThread21_0(td);
mt0.setName("A");
MyThread21_1 mt1 = new MyThread21_1(td);
mt1.setName("B");
mt0.start();
Thread.sleep(100);
mt1.start();
}
}
輸出結果如下
synchronized threadName = A, i = 1
synchronized threadName = A, i = 2
synchronized threadName = A, i = 3
synchronized threadName = A, i = 4
synchronized threadName = A, i = 5
synchronized threadName = A, i = 6
synchronized threadName = A, i = 7
synchronized threadName = A, i = 8
synchronized threadName = A, i = 9
synchronized threadName = A, i = 10
synchronized threadName = A, i = 11
synchronized threadName = A, i = 12
synchronized threadName = A, i = 13
synchronized threadName = A, i = 14
synchronized threadName = A, i = 15
synchronized threadName = A, i = 16
synchronized threadName = A, i = 17
synchronized threadName = A, i = 18
synchronized threadName = A, i = 19
synchronized threadName = A, i = 20
synchronized threadName = A, i = 21
synchronized threadName = A, i = 22
synchronized threadName = A, i = 23
......
synchronized threadName = A, i = 89
synchronized threadName = A, i = 90
synchronized threadName = A, i = 91
synchronized threadName = A, i = 92
synchronized threadName = A, i = 93
synchronized threadName = A, i = 94
synchronized threadName = A, i = 95
synchronized threadName = A, i = 96
synchronized threadName = A, i = 97
synchronized threadName = A, i = 98
synchronized threadName = A, i = 99
synchronized threadName = A, i = 100
----------run--otherMethod
可以看到,otherMethod方法被doLongTask方法阻塞,說明synchronized鎖定了整個物件,把otherMethod方法synchronized關鍵字去掉,輸出結果如下
synchronized threadName = A, i = 1
synchronized threadName = A, i = 2
synchronized threadName = A, i = 3
synchronized threadName = A, i = 4
synchronized threadName = A, i = 5
synchronized threadName = A, i = 6
synchronized threadName = A, i = 7
synchronized threadName = A, i = 8
synchronized threadName = A, i = 9
synchronized threadName = A, i = 10
synchronized threadName = A, i = 11
synchronized threadName = A, i = 12
synchronized threadName = A, i = 13
synchronized threadName = A, i = 14
synchronized threadName = A, i = 15
synchronized threadName = A, i = 16
synchronized threadName = A, i = 17
synchronized threadName = A, i = 18
synchronized threadName = A, i = 19
synchronized threadName = A, i = 20
synchronized threadName = A, i = 21
----------run--otherMethod
......
synchronized threadName = A, i = 72
synchronized threadName = A, i = 73
synchronized threadName = A, i = 74
synchronized threadName = A, i = 75
synchronized threadName = A, i = 76
synchronized threadName = A, i = 77
synchronized threadName = A, i = 78
synchronized threadName = A, i = 79
synchronized threadName = A, i = 80
synchronized threadName = A, i = 81
synchronized threadName = A, i = 82
synchronized threadName = A, i = 83
synchronized threadName = A, i = 84
synchronized threadName = A, i = 85
synchronized threadName = A, i = 86
synchronized threadName = A, i = 87
synchronized threadName = A, i = 88
synchronized threadName = A, i = 89
synchronized threadName = A, i = 90
synchronized threadName = A, i = 91
synchronized threadName = A, i = 92
synchronized threadName = A, i = 93
synchronized threadName = A, i = 94
synchronized threadName = A, i = 95
synchronized threadName = A, i = 96
synchronized threadName = A, i = 97
synchronized threadName = A, i = 98
synchronized threadName = A, i = 99
synchronized threadName = A, i = 100
otherMethod方法非同步執行了。
結論如下:
不同的方法被synchronized修飾會產生互斥,synchronized鎖住的是物件;
synchronized方法塊和synchronized方法效果一致;
synchronized(非this物件)
ThreadDomain22類
public class ThreadDomain22 {
private String userNameParam;
private String passwordParam;
private String anyString = new String();
public void setUserNamePassword(String userName, String password)
{
try
{
synchronized (anyString)
{
System.out.println("執行緒名稱為:" + Thread.currentThread().getName() +
"在 " + new Date() + " 進入同步程式碼塊");
userNameParam = userName;
Thread.sleep(3000);
passwordParam = password;
System.out.println("執行緒名稱為:" + Thread.currentThread().getName() +
"在 " + new Date() + " 離開同步程式碼塊");
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
MyThread22_0類
public class MyThread22_0 extends Thread{
private ThreadDomain22 td;
public MyThread22_0(ThreadDomain22 td)
{
this.td = td;
}
public void run()
{
td.setUserNamePassword("A", "AA");
}
}
MyThread22_1類
public class MyThread22_1 extends Thread{
private ThreadDomain22 td;
public MyThread22_1(ThreadDomain22 td)
{
this.td = td;
}
public void run()
{
td.setUserNamePassword("B", "B");
}
}
main
public class MyThread22_main{
public static void main(String[] args)
{
ThreadDomain22 td = new ThreadDomain22();
MyThread22_0 mt0 = new MyThread22_0(td);
MyThread22_1 mt1 = new MyThread22_1(td);
mt0.start();
mt1.start();
}
}
輸出結果
執行緒名稱為:Thread-0在 Tue Jul 02 14:51:16 CST 2019 進入同步程式碼塊
執行緒名稱為:Thread-0在 Tue Jul 02 14:51:19 CST 2019 離開同步程式碼塊
執行緒名稱為:Thread-1在 Tue Jul 02 14:51:19 CST 2019 進入同步程式碼塊
執行緒名稱為:Thread-1在 Tue Jul 02 14:51:22 CST 2019 離開同步程式碼塊
synchronized(anyString)和synchronized(this)效果一致,anyString作為全域性變數是一個物件,如果放入try裡面,就變成了多個物件,變成了非同步執行。
再看一個例子
ThreadDomain23類
public class ThreadDomain23 {
public void testMethod1(MyObject mo)
{
try
{
synchronized (mo)
{
System.out.println("testMethod1__getLock time = " +
new Date() + ", run ThreadName = " +
Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("testMethod1__releaseLock time = " +
new Date() + ", run ThreadName = " +
Thread.currentThread().getName());
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
MyObject類
public class MyObject {
public synchronized void speedPrintString()
{
System.out.println("speedPrintString__getLock time = " +
new Date() + ", run ThreadName = " +
Thread.currentThread().getName());
System.out.println("speedPrintString__releaseLock time = " +
new Date() + ", run ThreadName = " +
Thread.currentThread().getName());
}
}
MyThread23_0類
public class MyThread23_0 extends Thread{
private ThreadDomain23 td;
private MyObject mo;
public MyThread23_0(ThreadDomain23 td, MyObject mo)
{
this.td = td;
this.mo = mo;
}
public void run()
{
td.testMethod1(mo);
}
}
MyThread23_1類
public class MyThread23_1 extends Thread {
private MyObject mo;
public MyThread23_1(MyObject mo)
{
this.mo = mo;
}
public void run()
{
mo.speedPrintString();
}
}
main方法
public class MyThread23_main {
public static void main(String[] args)
{
ThreadDomain23 td = new ThreadDomain23();
MyObject mo = new MyObject();
MyThread23_0 mt0 = new MyThread23_0(td, mo);
MyThread23_1 mt1 = new MyThread23_1(mo);
mt0.start();
mt1.start();
}
}
輸出結果如下
testMethod1__getLock time = Tue Jul 02 15:23:05 CST 2019, run ThreadName = Thread-0
testMethod1__releaseLock time = Tue Jul 02 15:23:10 CST 2019, run ThreadName = Thread-0
speedPrintString__getLock time = Tue Jul 02 15:23:10 CST 2019, run ThreadName = Thread-1
speedPrintString__releaseLock time = Tue Jul 02 15:23:10 CST 2019, run ThreadName = Thread-1
speedPrintString方法總是在testMethod1方法之後執行,說明ThreadDomain23獲得了鎖,mo物件中的syncronized方法被阻塞,執行完testMethod1方法才會執行speedPrintString方法。
即當一個執行緒呼叫該方法時,其他執行緒阻塞。
結論:
1.當多個執行緒同時執行synchronized(非this物件){}同步程式碼塊時呈同步效果
2.當其他執行緒執行非this物件中的synchronized同步方法時呈同步效果
synchronized靜態方法
synchronized static可以對當前的類加鎖,即類鎖。
ThreadDomain24類
public class ThreadDomain24 {
public synchronized static void printA()
{
try
{
System.out.println("執行緒名稱為:" + Thread.currentThread().getName() +
"在" + System.currentTimeMillis() + "進入printA()方法");
Thread.sleep(3000);
System.out.println("執行緒名稱為:" + Thread.currentThread().getName() +
"在" + System.currentTimeMillis() + "離開printA()方法");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public synchronized static void printB()
{
System.out.println("執行緒名稱為:" + Thread.currentThread().getName() +
"在" + System.currentTimeMillis() + "進入printB()方法");
System.out.println("執行緒名稱為:" + Thread.currentThread().getName() +
"在" + System.currentTimeMillis() + "離開printB()方法");
}
public synchronized void printC()
{
System.out.println("執行緒名稱為:" + Thread.currentThread().getName() +
"在" + System.currentTimeMillis() + "進入printC()方法");
System.out.println("執行緒名稱為:" + Thread.currentThread().getName() +
"在" + System.currentTimeMillis() + "離開printC()方法");
}
}
MyThread24_0類,靜態方法
public class MyThread24_0 extends Thread{
public void run()
{
ThreadDomain24.printA();
}
}
MyThread24_1類,靜態方法
public class MyThread24_1 extends Thread {
public void run()
{
ThreadDomain24.printB();
}
}
MyThread24_2類,例項化呼叫方法
public class MyThread24_2 extends Thread{
private ThreadDomain24 td;
public MyThread24_2(ThreadDomain24 td)
{
this.td = td;
}
public void run()
{
td.printC();
}
}
main方法
public class MyThread24_main {
public static void main(String[] args)
{
ThreadDomain24 td = new ThreadDomain24();
MyThread24_0 mt0 = new MyThread24_0();
MyThread24_1 mt1 = new MyThread24_1();
MyThread24_2 mt2 = new MyThread24_2(td);
mt0.start();
mt1.start();
mt2.start();
}
}
輸出結果如下
執行緒名稱為:Thread-0在1562057924755進入printA()方法
執行緒名稱為:Thread-2在1562057924755進入printC()方法
執行緒名稱為:Thread-2在1562057924755離開printC()方法
執行緒名稱為:Thread-0在1562057927755離開printA()方法
執行緒名稱為:Thread-1在1562057927755進入printB()方法
執行緒名稱為:Thread-1在1562057927755離開printB()方法
可以看到,printA方法(類鎖)和printB方法(類鎖)同步執行,printC方法(物件鎖)非同步執行,printA方法和printB方法互斥,printC不受類鎖影響。
結論:
靜態方法持有類鎖,非靜態方法持有物件鎖,兩者互不干擾。