1. 什麼叫線程安全?java
多線程對共享資源進行寫的操做,受到其餘線程的干擾,致使數據偶問題,這種現象叫作線程安全問題。安全
package com.cn.test.thread; public class TrainThread implements Runnable { /** * 兩個線程進行售票100張 */ private int trainTicket = 100; @Override public void run() { while (trainTicket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } // 進行售票 System.out.println(Thread.currentThread().getName() + "開始售票:" + (100 - trainTicket + 1) + "張"); trainTicket--; } } public static void main(String[] args) { TrainThread train = new TrainThread(); Thread t1 = new Thread(train,"窗口1"); Thread t2 = new Thread(train,"窗口2"); t1.start(); t2.start(); } }
運行結果:多線程
窗口1開始售票:1張
窗口2開始售票:1張
窗口1開始售票:3張
窗口2開始售票:3張
窗口1開始售票:5張
窗口2開始售票:5張
窗口1開始售票:7張
窗口2開始售票:7張
窗口2開始售票:9張
窗口1開始售票:9張
窗口2開始售票:11張
窗口1開始售票:12張
窗口2開始售票:13張
窗口1開始售票:14張
窗口2開始售票:15張
窗口1開始售票:16張
窗口2開始售票:17張
窗口1開始售票:18張
窗口2開始售票:19張
窗口1開始售票:20張
窗口2開始售票:21張
窗口1開始售票:22張
窗口2開始售票:23張
窗口1開始售票:24張
窗口2開始售票:25張
窗口1開始售票:26張
窗口2開始售票:27張
窗口1開始售票:28張
窗口2開始售票:29張
窗口1開始售票:30張
窗口2開始售票:31張
窗口1開始售票:32張
窗口2開始售票:33張
窗口1開始售票:34張
窗口2開始售票:35張
窗口1開始售票:36張
窗口2開始售票:37張
窗口1開始售票:38張
窗口2開始售票:39張
窗口1開始售票:40張
窗口2開始售票:41張
窗口1開始售票:42張
窗口2開始售票:43張
窗口1開始售票:44張
窗口2開始售票:45張
窗口1開始售票:46張
窗口2開始售票:47張
窗口1開始售票:48張
窗口2開始售票:49張
窗口1開始售票:50張
窗口2開始售票:51張
窗口1開始售票:52張
窗口2開始售票:53張
窗口1開始售票:54張
窗口2開始售票:55張
窗口1開始售票:56張
窗口2開始售票:57張
窗口1開始售票:58張
窗口2開始售票:59張
窗口1開始售票:60張
窗口2開始售票:61張
窗口1開始售票:62張
窗口2開始售票:63張
窗口1開始售票:64張
窗口2開始售票:65張
窗口1開始售票:66張
窗口2開始售票:67張
窗口1開始售票:68張
窗口2開始售票:69張
窗口1開始售票:70張
窗口2開始售票:71張
窗口1開始售票:72張
窗口2開始售票:73張
窗口1開始售票:74張
窗口2開始售票:75張
窗口1開始售票:76張
窗口2開始售票:77張
窗口1開始售票:78張
窗口2開始售票:79張
窗口1開始售票:80張
窗口2開始售票:81張
窗口1開始售票:82張
窗口2開始售票:83張
窗口1開始售票:84張
窗口2開始售票:85張
窗口1開始售票:86張
窗口2開始售票:87張
窗口1開始售票:88張
窗口2開始售票:89張
窗口1開始售票:90張
窗口2開始售票:91張
窗口1開始售票:92張
窗口2開始售票:93張
窗口1開始售票:94張
窗口2開始售票:95張
窗口1開始售票:96張
窗口2開始售票:97張
窗口1開始售票:98張
窗口2開始售票:99張
窗口1開始售票:100張
窗口2開始售票:101張
線程安全解決的辦法:併發
使用多線程之間同步synchronized或使用鎖(lock)。ide
爲何使用線程同步或使用鎖能解決線程安全問題呢?函數
將可能發生線程安全的代碼,在同一時刻只有一個線程對此進行操做,代碼執行完畢以後釋放鎖以後才讓其餘線程併發執行,這樣就會解決線程安全的問題。spa
什麼是線程同步?線程
多個線程共享同一個資源,不會受到其餘線程的干擾。code
解決辦法1:對象
同步代碼塊。 將可能發生線程安全的代碼用同步代碼塊包裹起來。
synchronized(同一個數據) {
可能會發生線程衝突問題
}
同步代碼塊須要傳遞的對象(鎖對象):就是鎖住這個對象,表示這個對象正在爲我服務,其餘人不能用(非synchronized代碼塊、方法除外)。
while (trainTicket > 0) { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj) { // 進行售票 if (trainTicket > 0) { System.out.println(Thread.currentThread().getName() + "開始售票:" + (100 - trainTicket + 1) + "張"); trainTicket--; } } }
解決辦法2:
同步函數,在發生線程安全的函數中加入sychnorized關鍵字。
靜態同步函數:
方法上加上static關鍵字,使用synchronized 關鍵字修飾 或者使用類.class文件。靜態的同步函數使用的鎖是 該函數所屬字節碼文件對象能夠用 getClass方法獲取,也能夠用當前 類名.class 表示。
死鎖:
同步中嵌套同步,致使鎖沒法釋放
package com.cn.test.thread; public class TestDeadThread implements Runnable { private boolean flag = true; private static int trainCount = 100; private Object mutex = new Object(); @Override public void run() { if (flag) { while(true) { synchronized (mutex) { sale(); } } } else { while (true) { sale(); } } } private synchronized void sale() { synchronized (mutex) { if (trainCount > 0) { try { Thread.sleep(40); } catch (Exception e) { } System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "張票."); trainCount--; } } } public static void main(String[] args) throws InterruptedException { TestDeadThread test1 = new TestDeadThread(); Thread t1 = new Thread(test1,"thread-1"); Thread t2 = new Thread(test1,"thread-2"); t1.start(); Thread.sleep(40); test1.flag = false; t2.start(); } }
運行結果:
thread-1,出售 第1張票. thread-1,出售 第2張票. thread-1,出售 第3張票. thread-1,出售 第4張票. thread-1,出售 第5張票. thread-1,出售 第6張票. thread-1,出售 第7張票. thread-1,出售 第8張票. thread-1,出售 第9張票. thread-1,出售 第10張票. thread-1,出售 第11張票. thread-1,出售 第12張票. thread-1,出售 第13張票. thread-1,出售 第14張票. thread-1,出售 第15張票. thread-1,出售 第16張票. thread-1,出售 第17張票. thread-1,出售 第18張票. thread-1,出售 第19張票. thread-1,出售 第20張票. thread-1,出售 第21張票. thread-1,出售 第22張票. thread-1,出售 第23張票. thread-1,出售 第24張票. thread-1,出售 第25張票.