1. 程式人生 > >執行緒共享資料的安全問題和死鎖問題

執行緒共享資料的安全問題和死鎖問題

多執行緒訪問共享變數的時候會出現執行緒安全的問題

解決執行緒安全的問題:執行緒同步(同步程式碼塊、同步方法、同步鎖)

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......