線程安全
- 線程安全概念:當多個線程訪問某一個類(對象或方法)時,這個類始終都能表現出正確的行為,那么這個類(對象或方法)就是線程安全的。
- synchronized:可以在任意對象及方法上加鎖,而加鎖的這段代碼稱為“互斥區”或“臨界區”。
- 當多個線程訪問myThread的run方法時,以排隊的方式進行處理(這里排對是按照CPU分配的先后順序而定的),一個線程想要執行synchronized修飾的方法里的代碼,首先是嘗試獲得鎖,如果拿到鎖,執行synchronized代碼體內容;拿不到鎖,這個線程就會不斷的嘗試獲得這把鎖,直到拿到為止,而且是多個線程同時去競爭這把鎖。(也就是會有鎖競爭的問題)
線程不安全
package com.example.core.safely;public class MyThread extends Thread{private int count = 5;public void run(){count--;System.out.println(this.currentThread().getName() + "count = "+count);}public static void main(String[] args) {MyThread my = new MyThread();Thread t1 = new Thread(my,"t1");Thread t2 = new Thread(my,"t2");Thread t3 = new Thread(my,"t3");Thread t4 = new Thread(my,"t4");Thread t5 = new Thread(my,"t5");t1.start();t2.start();t3.start();t4.start();t5.start();}
}
線程安全 -> 添加synchronized
- synchronized 同步的概念就是共享,我們要牢牢記住"共享"這倆個字,如果不是共享的資源,就沒有必要進行同步。
- 異步:asynchronized 異步的概念就是獨立,相互之間不受到任何制約。就好像我們學習http的時候,在頁面發起的Ajax請求,我們還可以繼續瀏覽或操作頁面的內容,二者之間沒有任何關系
- 同步的目的就是為了線程安全,其實對于線程安全來說,需要滿足倆個特性: 原子性(同步) 可見性?? ?
package com.example.core.safely;public class MyThread extends Thread{private int count = 5;public synchronized void run(){count--;System.out.println(this.currentThread().getName() + "count = "+count);}public static void main(String[] args) {MyThread my = new MyThread();Thread t1 = new Thread(my,"t1");Thread t2 = new Thread(my,"t2");Thread t3 = new Thread(my,"t3");Thread t4 = new Thread(my,"t4");Thread t5 = new Thread(my,"t5");t1.start();t2.start();t3.start();t4.start();t5.start();}
}
線程之間的通信
- 線程通信概念:線程是操作系統中獨立的個體,但這些個體如果不經過特殊的處理就不能成為一個整體,線程間的通信就成為整體的必用方式之一。當線程存在通信指揮,系統間的交互性會更強大,在提高CPU利用率的同時還會使開發人員對線程任務在處理的過程中進行有效的把控與監督
- 使用wait / notify 方法實現線程間的通信。(注意這兩個方法都是object的類的方法,換句話說java為所有的對象都提供了這兩個方法) wait 和 notify 必須配合 synchronized 關鍵字使用 wait方法釋放鎖,notify方法不釋放鎖
ThreadLocal
- 線程局部變量,是一種多線程間并發訪問變量的解決方案。與其synchronized等加鎖的方式不同,ThreadLocal完全不提供鎖,而使用以空間換時間的手段,為每個線程提供變量的獨立副本,以保障線程安全
- 從性能上說,ThreadLocal不具有絕對的優勢,在并發不是很高的時候,加鎖的性能會更好,但作為一套與鎖完全無關的線程安全解決方案,在高并發量或者競爭激烈的場景,使用ThreadLocal可以在一定程度上減少鎖競爭
package com.example.core.safely;public class UseThreadLocal {public static ThreadLocal<String> threadLocal = new ThreadLocal<>();public void setThreadLocal(String value){threadLocal.set(value);}public String getThreadLocal(){return threadLocal.get();}public static void main(String[] args) {UseThreadLocal utl = new UseThreadLocal();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {utl.setThreadLocal("張三");System.err.println("當前t1:"+utl.getThreadLocal());}},"t1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {utl.setThreadLocal("李四");System.err.println("當前t2:"+utl.getThreadLocal());}},"t2");try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}t1.start();t2.start();System.out.println("當前值"+utl.getThreadLocal());}
}
問題
- 如題,有兩個線程A、B,A線程向一個集合(List<String>) 里面依次添加元素“abc”字符串, 一共添加十次,當添加到第五次的時候,希望B線程能夠收到A線程的通知,然后B線程執行相關的業務操作,我們應該如何進行設計?
1,使用CountDownLatch
-
latch.countDown();//喚醒通知
-
latch.await();//阻塞
package com.example.core.safely;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;public class ListAdd2 {// 1 定義的承裝字符串的容器private static List list = new ArrayList(); // 2 追加方法public void add(){list.add("bfxy");}public int size(){return list.size();}public static void main(String[] args) {final ListAdd2 list1 = new ListAdd2();final CountDownLatch latch = new CountDownLatch(1);//CountDownLatch讓一個線程阻塞,另一個線程運行,(1)是通知的個數// 線程AThread A = new Thread(new Runnable() {@Overridepublic void run() {try {for(int i = 0; i <10; i++){list1.add();System.out.println("當前線程:" + Thread.currentThread().getName() + ", 添加了一個元素..");Thread.sleep(500);if(list.size() == 5) {System.err.println("已經發出了喚醒通知!");latch.countDown();//喚醒通知}} } catch (InterruptedException e) {e.printStackTrace();}}}, "A");// 線程BThread B = new Thread(new Runnable() {@Overridepublic void run() {while(true){if(list1.size() != 5) {try {latch.await();//阻塞} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("當前線程收到通知:" + Thread.currentThread().getName() + " list size = 5 線程停止..");throw new RuntimeException();}}}, "B"); B.start();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}A.start();}}
2,使用volalite關鍵字
package com.example.core.safely;import java.util.ArrayList;
import java.util.List;public class ListAdd3 {// 1 定義的承裝字符串的容器private volatile static List list = new ArrayList(); // 2 追加方法public void add(){list.add("bfxy");}public int size(){return list.size();}public static void main(String[] args) {final ListAdd3 list1 = new ListAdd3();// 線程AThread A = new Thread(new Runnable() {@Overridepublic void run() {try {for(int i = 0; i <10; i++){list1.add();System.out.println("當前線程:" + Thread.currentThread().getName() + ", 添加了一個元素..");Thread.sleep(500);} } catch (InterruptedException e) {e.printStackTrace();}}}, "A");// 線程BThread B = new Thread(new Runnable() {@Overridepublic void run() {while(true){if(list1.size() == 5){System.out.println("當前線程收到通知:" + Thread.currentThread().getName() + " list size = 5 線程停止..");throw new RuntimeException();}}}}, "B"); B.start();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}A.start();}}
3,使用synchronized配合使用wait和notify
package com.example.core.safely;import java.util.ArrayList;
import java.util.List;public class ListAdd1 {// 1 定義的承裝字符串的容器private static List list = new ArrayList(); // 2 追加方法public void add(){list.add("bfxy");}public int size(){return list.size();}public static void main(String[] args) {final ListAdd1 list1 = new ListAdd1();final Object lock = new Object();// 線程AThread A = new Thread(new Runnable() {@Overridepublic void run() {try {synchronized (lock) {for(int i = 0; i <10; i++){list1.add();System.out.println("當前線程:" + Thread.currentThread().getName() + ", 添加了一個元素..");Thread.sleep(500);if(list.size() == 5) {System.err.println("已經發出了喚醒通知!");lock.notify();}} }} catch (InterruptedException e) {e.printStackTrace();}}}, "A");// 線程BThread B = new Thread(new Runnable() {@Overridepublic void run() {while(true){synchronized (lock) {if(list1.size() != 5) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}}//if(list1.size() == 5){System.out.println("當前線程收到通知:" + Thread.currentThread().getName() + " list size = 5 線程停止..");throw new RuntimeException();//}}}}, "B"); B.start();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}A.start();}}
?