并发包使用和解释
什么是并发包(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(); } }