執行緒共享資料的安全問題和死鎖問題
阿新 • • 發佈:2018-12-31
多執行緒訪問共享變數的時候會出現執行緒安全的問題
解決執行緒安全的問題:執行緒同步(同步程式碼塊、同步方法、同步鎖)
1.同步程式碼塊
synchronized(同步監視器){
//需要訪問的共享資料
}
同步監視器 : 俗稱“鎖”。可以使用任何物件充當。但是必須確定多個執行緒持有同一把鎖(同一個物件)
2.同步方法
同步方法: ---- 隱式的鎖 : this ----如果同步方法被靜態(static)修飾後,隱式的鎖是該類所屬的位元組碼檔案xxx.class(因為被static修飾,沒有物件可呼叫此方法) public synchronized void show(){ //需要訪問的共享資料 }
3.同步鎖
public class MyRunnable implements Runnable { //static修飾 保證是共享資源 多個執行緒構造器傳入同一個此物件則不必是static也可以 public static int tick = 100; //建立一個鎖 static修飾 保證是一個鎖 多個執行緒構造器傳入同一個此物件則不必是static也可以 public static Lock lock = new ReentrantLock(); @Override public void run() { while (true) { //上鎖 lock.lock(); try { if (tick > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { } tick--; System.out.println(Thread.currentThread().getName() + "完成售票,餘票為 :" + tick); } } finally { //必須手動解鎖 lock.unlock(); } } } }
public class TestLock {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Thread thread1 = new Thread(new MyRunnable(),"aa");
thread1.start();
Thread thread2 = new Thread(new MyRunnable(),"bb");
thread2.start();
}
}
public class TestThreadDeadLock { public static void main(String[] args) throws InterruptedException, ExecutionException { MyRunnable myRunnable = new MyRunnable(); //下面兩個執行緒構造器都傳入同一個myRunnable 則不必用上面說的static修飾共享變數 Thread thread1 = new Thread(myRunnable, "aa"); thread1.start(); Thread thread2 = new Thread(myRunnable, "bb"); thread2.start(); } }
列印結果
aa完成售票,餘票為 :99
aa完成售票,餘票為 :98
bb完成售票,餘票為 :97
bb完成售票,餘票為 :96
aa完成售票,餘票為 :95
aa完成售票,餘票為 :94
aa完成售票,餘票為 :93
aa完成售票,餘票為 :92
bb完成售票,餘票為 :91
aa完成售票,餘票為 :90
bb完成售票,餘票為 :89
aa完成售票,餘票為 :88
bb完成售票,餘票為 :87
bb完成售票,餘票為 :86
bb完成售票,餘票為 :85
bb完成售票,餘票為 :84
aa完成售票,餘票為 :83
aa完成售票,餘票為 :82
bb完成售票,餘票為 :81
bb完成售票,餘票為 :80
bb完成售票,餘票為 :79
bb完成售票,餘票為 :78
bb完成售票,餘票為 :77
bb完成售票,餘票為 :76
aa完成售票,餘票為 :75
aa完成售票,餘票為 :74
aa完成售票,餘票為 :73
aa完成售票,餘票為 :72
aa完成售票,餘票為 :71
aa完成售票,餘票為 :70
aa完成售票,餘票為 :69
aa完成售票,餘票為 :68
aa完成售票,餘票為 :67
aa完成售票,餘票為 :66
aa完成售票,餘票為 :65
aa完成售票,餘票為 :64
aa完成售票,餘票為 :63
aa完成售票,餘票為 :62
aa完成售票,餘票為 :61
aa完成售票,餘票為 :60
aa完成售票,餘票為 :59
aa完成售票,餘票為 :58
aa完成售票,餘票為 :57
bb完成售票,餘票為 :56
bb完成售票,餘票為 :55
bb完成售票,餘票為 :54
bb完成售票,餘票為 :53
bb完成售票,餘票為 :52
bb完成售票,餘票為 :51
bb完成售票,餘票為 :50
bb完成售票,餘票為 :49
bb完成售票,餘票為 :48
bb完成售票,餘票為 :47
bb完成售票,餘票為 :46
bb完成售票,餘票為 :45
bb完成售票,餘票為 :44
bb完成售票,餘票為 :43
bb完成售票,餘票為 :42
bb完成售票,餘票為 :41
bb完成售票,餘票為 :40
bb完成售票,餘票為 :39
bb完成售票,餘票為 :38
bb完成售票,餘票為 :37
bb完成售票,餘票為 :36
bb完成售票,餘票為 :35
bb完成售票,餘票為 :34
aa完成售票,餘票為 :33
aa完成售票,餘票為 :32
aa完成售票,餘票為 :31
aa完成售票,餘票為 :30
aa完成售票,餘票為 :29
aa完成售票,餘票為 :28
bb完成售票,餘票為 :27
bb完成售票,餘票為 :26
bb完成售票,餘票為 :25
bb完成售票,餘票為 :24
bb完成售票,餘票為 :23
aa完成售票,餘票為 :22
aa完成售票,餘票為 :21
aa完成售票,餘票為 :20
aa完成售票,餘票為 :19
aa完成售票,餘票為 :18
aa完成售票,餘票為 :17
aa完成售票,餘票為 :16
aa完成售票,餘票為 :15
aa完成售票,餘票為 :14
aa完成售票,餘票為 :13
aa完成售票,餘票為 :12
aa完成售票,餘票為 :11
aa完成售票,餘票為 :10
aa完成售票,餘票為 :9
aa完成售票,餘票為 :8
aa完成售票,餘票為 :7
aa完成售票,餘票為 :6
aa完成售票,餘票為 :5
aa完成售票,餘票為 :4
bb完成售票,餘票為 :3
bb完成售票,餘票為 :2
bb完成售票,餘票為 :1
bb完成售票,餘票為 :0
執行緒的死鎖 : 不同的執行緒分別佔用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了執行緒的死鎖
匿名內部類寫一個死鎖
public class TestThreadDeadLock {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//鎖1 內部類如果引用外部類的變,則該變數必須為final,這是規定
final Object obj1 = new Object();
//鎖2
final Object obj2 = new Object();
//執行緒1
new Thread(){
public void run(){
synchronized (obj1) {
System.out.println("獲取資源1,等待資源2......");
try {
//執行緒1獲取鎖1後 睡一會(此時執行緒1佔用鎖1),等待執行緒2獲取鎖2(執行緒2佔用鎖2)
Thread.sleep(200);
} catch (InterruptedException e) {
}
synchronized (obj2) {
System.out.println("--------------------------------");
}
}
}
}.start();
// Thread.sleep(5000);
//執行緒2
new Thread() {
public void run() {
synchronized (obj2) {
System.out.println("獲取資源2,等待資源1......");
synchronized (obj1) {
System.out.println("****************************************");
}
}
}
}.start();
}
}
獲取資源1,等待資源2......
獲取資源2,等待資源1......
程式碼寫一個死鎖
public class MyRunnable implements Runnable {
//鎖1 必須是static修飾的 代表同一把鎖
public static Object obj1 = new Object();
//鎖2
public static Object obj2 = new Object();
public boolean flag = true;
@Override
public void run() {
if(flag){
synchronized (obj1) {
System.out.println("獲取資源1,等待資源2......");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
System.out.println("--------------------------");
}
}
}else{
synchronized (obj2) {
System.out.println("獲取資源2,等待資源1......");
synchronized (obj1) {
System.out.println("**************************");
}
}
}
}
}
public class TestThreadDeadLock {
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyRunnable myRunnable1 = new MyRunnable();
Thread thread1 = new Thread(myRunnable1);
thread1.start();
MyRunnable myRunnable2 = new MyRunnable();
myRunnable2.flag = false;
Thread thread2 = new Thread(myRunnable2);
thread2.start();
}
}
獲取資源1,等待資源2......
獲取資源2,等待資源1......