上次學習了線程的建立,已級簡單應用,此次來看看線程安全,及同步鎖 synchronized 的做用:java
package thread; /** * Synchronized 同步鎖, 線程安全; * * 買票舉例測試 **/ public class MyTheard3 { /** * 知識點: * 1, 什麼是線程安全? * 線程安全: 就是當 多個線程 同時共享同一個 全局變量 的時候,作 寫 的操做時,數據的不可控性; * * 2, 什麼是線程同步? * 線程同步,是爲了保證數據的原子性;保證數據的安全; * * 3, 同步鎖: * synchronized(Object) { * 包含須要同步的代碼,這個地方一次只能有一個線程執行; * } * * 4, 同步的前提: * (1), 必需要有兩個或兩個以上的線程; * (2), 必須是多個線程使用同一把鎖; * * 5, 同步鎖的做用: * 保證同步中只能有一個線程運行; * * 6, 原理: * (1), 當多個線程同時執行時,若是有一個線程搶到了鎖,那麼該線程執行,其餘線程就算有CPU執行權也要等待,等搶到鎖的線程釋放了鎖後,其餘線程在繼續搶鎖; * (鎖就至關於一個身份令牌,有鎖才能執行,而鎖只有一把)(舉例: 搶廁所) * * (2), 只有當代碼執行完畢,或者拋出異常時,纔會釋放鎖; * * * 7, 優勢: 解決了線程安全問題; * * 8, 缺點: 須要搶鎖,消耗CPU資源,會發生死鎖問題; * * */ public static void main(String[] args) { // 兩個線程必須使用同一個run() Send1 send = new Send1(); Thread send1 = new Thread(send,"窗口1:"); Thread send2 = new Thread(send,"窗口2:"); send1.start(); send2.start(); } } /** * 售票線程 * */ class Send1 implements Runnable{ /*總票數,線程的全局變量*/ private static int total = 100; /*線程中的鎖*/ Object object = new Object(); @Override public void run() { while(total>0){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } stale(); } } private void stale(){ //同步鎖: synchronized (object) { //每次只能有一個線程執行 if(total>0){ System.out.println(Thread.currentThread().getName()+"賣出第"+((100-total)+1)+"張票"); total--; } } } }
2, 使用this鎖:安全
private void stale(){ //同步鎖 this : synchronized (this) { //每次只能有一個線程執行 if(total>0){ System.out.println(Thread.currentThread().getName()+"賣出第"+((100-total)+1)+"張票"); total--; } } }
3, 使用同步函數:多線程
//同步函數: (synchronized修飾的方法) private synchronized void stale(){ //每次只能有一個線程執行 if(total>0){ System.out.println(Thread.currentThread().getName()+"賣出第"+((100-total)+1)+"張票"); total--; } }
4: 同步函數使用的就是this鎖;併發
5: 總結: 同步函數和同步代碼塊能夠實現同步,只要同步代碼塊使用this鎖便可;ide
6: 靜態同步函數:函數
//靜態同步函數:(static synchronized修飾的方法, 使用 Send1.class 鎖) private static synchronized void stale2(){ //每次只能有一個線程執行 if(total>0){ System.out.println(Thread.currentThread().getName()+"賣出第"+((100-total)+1)+"張票"); total--; } }
靜態同步函數使用 當前類的字節碼 鎖;學習
7: 總結 : 同步函數和靜態同步函數不能實現同步;測試
8: 多線程的可見性: volatile;this
package thread; import java.util.concurrent.atomic.AtomicInteger; /** * 多線程的可見性 * * */ public class MyTheard4 { /** * 知識點: * * 1, 多線程的三大特性 : 原子性; 可見性; 有序性; * * 2, 什麼是JAVA內存模型? * JAVA內存模型分爲: 主內存(主要存放共享的全局變量), 私有內存(該線程的私有變量); (屬於 多線程可見性JMM方面) * JAVA內存模型,決定一個線程與另外一個線程是否可見; (線程安全問題) * * 3, volatile 可見性; * * 4, AtomicInteger (JDK1.5併發包中的); * */ public static void main(String[] args) { TheardTest thread = new TheardTest(); Thread t1 = new Thread(thread,"T1"); Thread t2 = new Thread(thread,"T2"); Thread t3 = new Thread(thread,"T3"); Thread t4 = new Thread(thread,"T4"); Thread t5 = new Thread(thread,"T5"); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); /** * 運行結果: * T2::1923 * T3::2876 * T5::3876 * T1::1922 * T4::4876 * 結論 : volatile 不能保證原子性; * */ } } class TheardTest implements Runnable{ // 線程可見 private volatile static int count = 0; @Override public void run() { for(int i=0;i<1000;i++){ count++; } System.out.println(Thread.currentThread().getName()+"count::"+count); } }
9: 多線程可見性: AtomicIntegeratom
package thread; import java.util.concurrent.atomic.AtomicInteger; /** * 多線程的可見性 * * */ public class MyTheard4 { public static void main(String[] args) { TheardTest thread = new TheardTest(); Thread t1 = new Thread(thread,"T1"); Thread t2 = new Thread(thread,"T2"); Thread t3 = new Thread(thread,"T3"); Thread t4 = new Thread(thread,"T4"); Thread t5 = new Thread(thread,"T5"); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } } class TheardTest implements Runnable{ // 線程可見 private static AtomicInteger count2 = new AtomicInteger(0); @Override public void run() { for(int i=0;i<1000;i++){ //等同於count++ count2.incrementAndGet(); } System.out.println(Thread.currentThread().getName()+"count2::"+count2); } }
測試後,會發現: AtomicInteger能夠保證數據原子性;
10, ThreadLocal 爲每一個線程建立局部變量,私有數據
class TheardTest implements Runnable{ // 線程可見 private volatile static int count = 0; // 共享數據,保證數據原子性 private static AtomicInteger count2 = new AtomicInteger(0); // ThreadLocal: 爲每一個線程建立局部變量,私有數據 private static ThreadLocal<Integer> count3 = new ThreadLocal<Integer>(){ protected Integer initialValue() { return 0; }; }; @Override public void run() { for(int i=0;i<1000;i++){ count++; count2.incrementAndGet(); count3.set(count3.get()+1); } System.out.println(Thread.currentThread().getName()+"count::"+count); System.out.println(Thread.currentThread().getName()+"count2::"+count2); System.out.println(Thread.currentThread().getName()+"count3::"+count3.get()); } }
從打印結果能夠發現,使用ThreadLocal建立的共享變量並無共享;