JUC(3)List、Set、Map集合线程安全Callable创建线程三大工具类:CountDownLatch减法计数器、CyclicBarrier加法计数器、Semaphore计数信号量
1. List集合线程安全
- CopyOnWriteArrayList是线程安全的集合;
- ArrayList是线程不安全的集合;
- Vector是线程安全的集合(不推荐使用)
1.1 解决ArrarList线程不安全的方案:
- 使用Vector:
查看Vector的add()方法,发现相比于ArrayList的add()方法前面加了synchronized修饰,因此是线程安全的 - 使用Collectiobns.synchronizedlist(new ArrayList< String >):
Collections集合工具类提供一系列线程安全的集合构造方法。 - 使用 JUC包下的CopyOnWriteArrayList集合:
一种线程安全的集合,CopyOnWrite的意思是写入时复制,是一种计算机程序设计优化策略,解决多线程写入覆盖问题。
1.2 CopyOnWriteArrayList的add方法源码:
/*** Appends the specified element to the end of this list.** @param e element to be appended to this list* @return {@code true} (as specified by {@link Collection#add})*/public boolean add(E e) {final ReentrantLock lock = this.lock;lock.lock();// 加的是lock锁try {Object[] elements = getArray();int len = elements.length;Object[] newElements = Arrays.copyOf(elements, len + 1);newElements[len] = e;setArray(newElements);return true;} finally {lock.unlock();}}
CopyOnWriteArrayList的核心是 读写分离 Object[] newElements = Arrays.copyOf(elements, len + 1);通过复制之前的数组,并长度加一,添加加入的值,从而实现add方法。
2.Set集合线程安全
2.1 HashSet是线程不安全的,解决方案有两种:
- Collections.synchronizedSet():
Set set = Collections.synchronizedSet(new HashSet<>()); - CopyOnWriteArraySet:
Set set = new CopyOnWriteArraySet<>();
2.2HashSet的底层是什么?
/*** Constructs a new, empty set; the backing <tt>HashMap</tt> instance has* default initial capacity (16) and load factor (0.75).*/public HashSet() {map = new HashMap<>();}
从源码中可以看出HashSet底层就是HashMap。
/*** Adds the specified element to this set if it is not already present.* More formally, adds the specified element <tt>e</tt> to this set if* this set contains no element <tt>e2</tt> such that* <tt>(e==null ? e2==null : e.equals(e2))</tt>.* If this set already contains the element, the call leaves the set* unchanged and returns <tt>false</tt>.** @param e element to be added to this set* @return <tt>true</tt> if this set did not already contain the specified* element*/public boolean add(E e) {return map.put(e, PRESENT)==null;}
HashSet中add()方法,调用的还是HashMap的底层put()方法。
3.Map集合线程安全(重点)
HashMap源码分析:跳转链接
ConcurrentHashMap源码分析:链接跳转
4.Callable创建线程
对比Runnable,可以有返回值、有参数
使用步骤:资源类实现Callable接口,重写 T call() 方法,创建资源类实例mt ,然后new Thread(new FutureTask(mt)) .start()开启一个线程
public class Demo1_Callable {public static void main(String[] args) throws InterruptedException, ExecutionException {/*** 开启线程需要 中间类 FutureTask 类* 该类的·构造函数 可以 将 Callable 实例绑定,并且 该类实现 Runnable 接口*/MyThread mt = new MyThread();//1.for (int i = 0; i < 10; i++) {FutureTask target = new FutureTask(mt);//2.new Thread(target).start();//3.// 返回值System.out.println(target.get()); // 这个get方法可能会产生阻塞}}
}class MyThread implements Callable<String> {Lock lock = new ReentrantLock();@Overridepublic String call() {//lock.lock();try {System.out.println(Thread.currentThread().getName() + "====> test" );return "带返回值的Callable接口";// 唤醒//Condition condition = lock.newCondition();//condition.signal();} catch (Exception e) {e.printStackTrace();return null;} finally {//lock.unlock();}}
}
5、三大辅助工具类
5.1 CountDownLatch减法计数器类
CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。
栗子:
public class Demo1_CountDownLatch {public static void main(String[] args) throws InterruptedException {// 创建计数器类CountDownLatch countDownLatch = new CountDownLatch(5);for (int i = 0; i < 5; i++) {new Thread(()-> {System.out.println(Thread.currentThread().getName() +"===>执行完毕");countDownLatch.countDown();//数量减1},String.valueOf(i)).start();}// 等待上面线程全部执行完毕,才执行性下面代码countDownLatch.await();System.out.println("结束执行");}
}输出:
1===>执行完毕
4===>执行完毕
2===>执行完毕
3===>执行完毕
0===>执行完毕
结束执行
API
- countDownLatch.countDown() :数量减1
- countDownLatch.await():等待计数器归0,然后在向下执行。
CountDownLatch底层原理
CountDownLatch通过AQS(AbstractQueuedSynchronizer)里面的共享锁来实现的。
ReentrantLock也是使用AQS
5.3 CyclicBarrier加法计数器类 [ˈsaɪklɪk] [ˈbæriər]
达到指定目标个数线程执行完毕,才执行绑定的任务,否则会阻塞
public static void main(String[] args) {CyclicBarrier cyclicBarrier = new CyclicBarrier(6,()->{System.out.println("6个线程全部执行完才输出!");});for (int i = 1; i <= 10; i++) {final int temp = i;new Thread(()->{System.out.println(Thread.currentThread().getName() + "---执行完毕--");try {// 等待设置目标线程数达标,执行绑定的方法cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}},String.valueOf(temp)).start();}
}
输出结果:
2===>执行完毕~~
1===>执行完毕~~
3===>执行完毕~~
5===>执行完毕~~
4===>执行完毕~~
6===>执行完毕~~
6个线程全部执行完才输出!!
7===>执行完毕~~
8===>执行完毕~~
9===>执行完毕~~
10===>执行完毕~~此时程序还未停止
5.4 Semaphore计数信号量
概念
- 一个计数信号量。 在概念上,信号量维持一组许可证。
- 如果有必要,每个acquire()都会阻塞,直到许可证可用,然后才能使用它。
- 每个release()添加许可证,潜在地释放阻塞获取方。
- Semaphore只保留可用数量的计数,并相应地执行。
- 假如只有3个停车位,6辆车,无空车位之前其他三辆车需要等待
public static void main(String[] args) {// 可以理解为只有3个车位Semaphore semaphore = new Semaphore(3);for (int i = 1; i <= 8; i++) {new Thread(()->{try {// 占位semaphore.acquire();System.out.println(Thread.currentThread().getName() + "===>拿到了车位!");// 停车时间TimeUnit.SECONDS.sleep(3);// 离开车位System.out.println(Thread.currentThread().getName() + "====>离开了!");} catch (InterruptedException e) {e.printStackTrace();}finally {// 最后告诉等待的人有1个空位了semaphore.release();}},String.valueOf(i)).start();}
}
输出结果:
2===>拿到了车位!
3===>拿到了车位!
1===>拿到了车位!
1====>离开了!
3====>离开了!
2====>离开了!
5===>拿到了车位!
6===>拿到了车位!
4===>拿到了车位!
5====>离开了!
4====>离开了!
6====>离开了!
7===>拿到了车位!
8===>拿到了车位!
7====>离开了!
8====>离开了!
API
- semaphore.acquire():获得资源,如果资源已经没了,等待被释放为止
- semaphore.release():释放资源,资源量加1,唤醒等待的线程
JUC(3)List、Set、Map集合线程安全Callable创建线程三大工具类:CountDownLatch减法计数器、CyclicBarrier加法计数器、Semaphore计数信号量相关推荐
- 集合之Map家族的TreeMap + Sort +Properties及Collections工具类和总结
集合之Map家族的TreeMap + Sort +Properties及Collections工具类和总结 一.TreeMap 1.TreeMap的使用 import java.util.Arrays ...
- day14 线程池、原子性、并发工具类
目录 一.线程池 1.1 线程状态 1.2 线程池的基本原理 1.3 Executors默认线程池 1.4 创建指定上限的线程池 1.5 ThreadPoolExecutor(线程池执行器) 1.5. ...
- 线程了解以及创建线程的Threading模块中的部分方法
了解线程 1.什么是线程 在传统的操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程,其实就是一条流水线的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 车间负责把资源 ...
- Java—遍历集合的N种方式总结Collections工具类
遍历集合的N种方式总结 [示例1]遍历List方法1,使用普通for循环 for(int i=0;i<list.size();i++){ //list为集合的对象名 String ...
- Java JUC工具类--CountDownLatch
CountDownLatch:用于监听某些初始化操作,并且线程进行阻塞,等初始化执行完毕后,通知主线程继续工作执行 package com.example.core.juc;import java.u ...
- java 创建线程_java多线程|创建线程的各种方式
javaDEMO 本网站记录了最全的各种JavaDEMO ,保证下载,复制就是可用的,包括基础的, 集合的, spring的, Mybatis的等等各种,助力你从菜鸟到大牛,记得收藏哦~~https: ...
- JAVA线程并发数量控制_Java并发工具类(三):控制并发线程数的Semaphore
作用 Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源. 简介 Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数 ...
- 多线程(一) 线程概念及创建线程的方法
多线程(一) 多线程基础 前言 我只是一个新上路的小菜鸟,通过分享自己的所学来巩固知识,文中如有错误希望能够指出改正. 一.线程的相关概念 程序 程序是为了完成特定任务,用某种语言编写的一组指令的集合 ...
- 陈硕智能指针线程安全_C++ 创建线程的方法
C++开发过程中,创建线程是必不可少的,今天就给大家说说c++创建线程的方法,主要介绍我常用的2种方法. 第一种:CreateThread HANDLE WINAPI CreateThread( t ...
最新文章
- 预测人民币在2006年最终的收盘价
- 自定义ViewGroup实现ViewPager的滑动效果
- 计算机没有autoCAD_挑战在一年内用晚上业余时间学会灵活运用CAD(1)|cad|autocad|图学|计算机|电子电路...
- ArcGIS中生成蜂窝多边形算法解析
- 单片机c语言程序设计叶俊明,单片机C语言程序设计
- windows修改远程桌面RDP连接数
- socket阻塞与非阻塞,同步与异步
- SkyEye——汽车电子系统仿真测试工具
- python效率低为什么_为什么 Python 这么慢?
- libsvm2.89在matlab,libsvm-mat-2.89-3工具箱,方便实用
- 【转载】VC遍历文件夹下所有文件和文件夹
- 许久未见,归来仍是少年?
- cyclone小知识(二)——cyclone加载扫描工程的数据
- 萤火虫小巷2(看完了)
- 登陆远程kvm_KVM远程VMM管理
- SCI 投稿全过程信件模板一览
- 初识动态规划(一)简单入门动态规划与上手操作
- 上证指数(000001)股票历史数据,下载上证指数(000001历史数据
- Ubuntu系统界面启动后启动开机自启动后锁屏
- 堪称神级的Java技术栈手册火了!