什么是並發包(JDK1.5提出):收集了各種專門在多線程情況下使用,並且可以保證線程安全的一些類
public class CopyOnWrite { static List<Integer> list = new ArrayList<>(); public static void main(String[] args) { demo1(); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } public static void demo1(){ new Thread(){ @Override public void run() { for (int i = 0; i < 10000; i++) { list.add(i); } } }.start(); new Thread(){ @Override public void run() { for (int i = 0; i < 10000; i++) { list.add(i); } } }.start(); } }
結果:

解決方法使用:
public static CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
Set使用CopyOnWriteArraySet
public static CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<>();
Map
public static Hashtable<Integer, Integer> map = new Hashtable<>();
但是HashTable有兩個性能上的問題:
a.無腦加鎖,無論是添加,刪除,獲取都加鎖,並使用同一個鎖對象,導致性能極其低下
b.HashTable添加是全局鎖,有且僅有一個線程可以操作HashTable,導致性能極其低下
多線程並發問題關鍵字
自從JDK5發布以來,在java.util.concurrent包中提供了一些非常有用的輔助類來幫助我們進行並發編程,下面就介紹一下這些輔助類中的Semaphore、CyclicBarrier、CountDownLatch以及Exchanger的相關用法
1.
CountDownLatch可以實現類似計數器的功能,計數器的初始值為指定的線程的數量,每當一個線程完成了自己的任務,計數器的值就會減1,當計數器的值達到了0時,它表示所有的線程都完成了任務,然后在閉鎖上等待的線程就可以恢復執行任務。構造器上的計數值實際上就是閉鎖需要等待的線程數量,這個值只能被設置一次,而且CountDownLatch沒有提供任何機制去重新設置這個值。
構造方法: public CountDownLatch(int count);指定計數的線程 成員方法: public void await();讓當前線程等待 public void countDown();減少需要等待的線程數量
public class BengTest { static CountDownLatch downLatch = new CountDownLatch(1); static CountDownLatch downLatch2 = new CountDownLatch(1); public static void main(String[] args) { demo1(); } public static void demo1(){ new Thread(){ @Override public void run() { System.out.println("起床"); downLatch.countDown(); } }.start(); new Thread(){ @Override public void run() { try { downLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("洗臉"); downLatch2.countDown(); } }.start(); new Thread(){ @Override public void run() { try { downLatch2.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("出門"); } }.start(); } }
2.
CyclicBarrier直譯過來叫做內存屏障,它要做的事情是,讓一組線程到達一個屏障(也可以叫同步點)時被阻塞,直到最后一個線程到達屏障時,屏障才會開門,所有被屏障攔截的線程才會繼續下面的業務。
構造方法: public CyclicBarrier(int parties, Runnable barrierAction); 參數1:parties表示這組線程的數量! 參數2:barrierAction 表示一組線程都到達之后需要執行的任務! 成員方法: public int await(); 讓當前線程阻塞
/** * @program: study_java * @description: test * @author: xiaozhang6666 * @create: 2020-06-22 13:44 **/ public class BengTest { static CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() { @Override public void run() { System.out.println("觸發屏障后程序"); } }); public static void main(String[] args) { demo2(); } public static void demo2(){ new Thread(){ @Override public void run() { System.out.println("屏障1"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }.start(); new Thread(){ @Override public void run() { System.out.println("屏障2"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }.start(); } }
emaphore是計數信號量,是操作系統中的一個概念,經常用於限制獲取某種資源的線程數量,在new 這個類的時候需要給這個類傳遞一個參數permits,這個參數是整數類型,這個參數的意思是同一時間內,最多允許多少個線程同時執行acquire方法和release方法之間的代碼,如果方法acquire沒有參數則默認是一個許可。
構造方法: public Semaphore(int permits); 參數permits稱為許可證,即最大的線程並發數量 成員方法: public void acquire(); 表示獲取許可證 public void release(); 釋放許可證
package day17.package_homework; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Semaphore; /** * @program: study_java * @description: test * @author: xiaozhang6666 * @create: 2020-06-22 13:44 **/ public class BengTest { static Semaphore semaphore = new Semaphore(1); public static void main(String[] args) { demo3(); } public static void demo3() { new Thread() { @Override public void run() { try { semaphore.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程一開始"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程一結束..."); semaphore.release(); } }.start(); new Thread() { @Override public void run() { try { semaphore.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程二開始"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程二結束..."); semaphore.release(); } }.start(); new Thread() { @Override public void run() { try { semaphore.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程三開始"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程三結束..."); semaphore.release(); } }.start(); } }
4.Exchanger
Exchanger是用於線程間協作的工具類,用於線程間的數據交換,它提供一個同步點,在這個同步點兩個線程可以交換彼此的數據。這兩個線程通過exchange方法交換數據, 如果第一個線程先執行exchange方法,它會一直等待第二個線程也執行exchange,當兩個線程都到達同步點時,這兩個線程就可以交換數據,將本線程生產出來的數據傳遞給對方。
構造方法: public Exchanger(); 成員方法: public V exchange(V x);//交換數據
public class ThreadA extends Thread { private Exchanger<String> exchanger; public ThreadA(Exchanger<String> exchanger){ this.exchanger = exchanger; } @Override public void run() { try { //線程A給線程B發信息 System.out.println("線程A給線程B發信息..."); String exchange = exchanger.exchange("AAAAAAAAAAAA"); System.out.println("同時獲取到線程B的回信:"+exchange); }catch (Exception e){ e.printStackTrace(); } } } public class ThreadB extends Thread { private Exchanger<String> exchanger; public ThreadB(Exchanger<String> exchanger){ this.exchanger = exchanger; } @Override public void run() { try { //線程B給線程A發信息 System.out.println("線程B給線程A發信息..."); String exchange = exchanger.exchange("BBBBBBBBBBBBBBBB"); System.out.println("同時獲取到線程A的回信:"+exchange); }catch (Exception e){ e.printStackTrace(); } } } public class TestDemo { public static void main(String[] args) { //1.創建Exchanger Exchanger<String> exchanger = new Exchanger<String>(); //2.創建線程AB ThreadA a = new ThreadA(exchanger); ThreadB b = new ThreadB(exchanger); //3.開啟線程A a.start(); b.start(); } }
