什么是并发包(JDK1.5提出):
收集了各种专门在多线程情况下使用,并且可以保证线程安全的一些类
1. CopyOnWriteArrayList
ArrayList是线程不安全的
public class MyThread extends Thread {
public static ArrayList<Integer> list = new ArrayList<>();//线程不安全的
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
list.add(i);
}
System.out.println("添加完毕!");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
//1.创建线程
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
Thread.sleep(2000);
System.out.println(MyThread.list.size());
}
}
运行结果:
可能会出现异常,最后的结果小于20000个元素
CopyOnWriteArrayList是线程安全的
public class MyThread extends Thread {
// public static ArrayList list = new ArrayList<>();//线程不安全的
public static CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();//线程安全的
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
list.add(i);
}
System.out.println("添加完毕!");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
//1.创建线程
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
Thread.sleep(2000);
System.out.println(MyThread.list.size());
}
}
CopyOnWriteArrayList之所以是线程安全的,因为CopyOnWriteArrayList中的add方法是由如下代码实现的:先加锁,后执行代码,最后解锁
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
需要同步的代码
return true;
} finally {
lock.unlock();
}
}
2. CopyOnWriteArraySet
HashSet是线程不安全的
public class MyThread extends Thread {
public static HashSet<Integer> set = new HashSet<>();//线程不安全的
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
set.add(i);
}
System.out.println("子线程添加完毕!");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
//1.创建线程
MyThread mt = new MyThread();
mt.start();
for (int i = 10000; i < 20000; i++) {
MyThread.set.add(i);
}
System.out.println("主线程添加完毕..");
Thread.sleep(1000);
System.out.println(MyThread.set.size());
}
}
输出结果:
主线程添加完毕…
子线程添加完毕!
19405(每次基本都会小于20000)
CopyOnWriteArraySet是线程安全的
public class MyThread extends Thread {
// public static HashSet set = new HashSet<>();//线程不安全的
public static CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<>();//线程安全的
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
set.add(i);
}
System.out.println("子线程添加完毕!");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
//1.创建线程
MyThread mt = new MyThread();
mt.start();
for (int i = 10000; i < 20000; i++) {
MyThread.set.add(i);
}
System.out.println("主线程添加完毕..");
Thread.sleep(1000);
System.out.println(MyThread.set.size());
}
}
输出结果:
主线程添加完毕…
子线程添加完毕!
20000(始终都是20000!)
CopyOnWriteArraySet之所以是线程安全的, 因为如下代码:
public boolean add(E e) {
return al.addIfAbsent(e);
}
private boolean addIfAbsent(E e, Object[] snapshot) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
需要同步的代码;
return true;
} finally {
lock.unlock();
}
}
3. ConcurrentHashMap
HashMap是线程不安全的
public class MyThread extends Thread {
public static Map<Integer, Integer> map = new HashMap<>();//是线程不安全的
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
map.put(i, i);// 00 11 22 33 44 .. 9999 9999
}
System.out.println("1万次OK");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
//1.创建线程
MyThread mt = new MyThread();
mt.start();
for (int i = 10000; i < 20000; i++) {
MyThread.map.put(i, i);//10000 10000 ... 19999 19999
}
System.out.println("1万次OK");
Thread.sleep(1000);
System.out.println(MyThread.map.size());
}
}
输出结果:
1万次OK
1万次OK
19685(经常小于20000)
注意:
HashMap是线程不安全的,多个线程操作同一个HashMap时,可能会出现以下情况:
a.运行没问题,但是结果小于单线程情况下的结果(大概率出现这种情况)
b.假死状态(可能出现,概率比较小)
c.抛出异常(可能出现,概率比较小)
Hashtable是线程安全的,但效率低
public class MyThread extends Thread {
// public static HashMap map = new HashMap<>();//是线程不安全的
public static Hashtable<Integer, Integer> map = new Hashtable<>();//是线程安全的
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
map.put(i, i);// 00 11 22 33 44 .. 9999 9999
}
System.out.println("1万次OK");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
//1.创建线程
MyThread mt = new MyThread();
mt.start();
for (int i = 10000; i < 20000; i++) {
MyThread.map.put(i, i);//10000 10000 ... 19999 19999
}
System.out.println("1万次OK");
Thread.sleep(3000);
System.out.println(MyThread.map.size());
}
}
运行结果:
1万次OK
1万次OK
20000(结果始终是20000!)
HashTable之所以能保证线程安全,原因是如下代码:
public synchronized V put(K key, V value) {
需要同步的代码;
}
public synchronized V remove(Object key) {
需要同步的代码;
}
public synchronized V get(Object key) {
需要同步的代码;
}
但是HashTable有两个性能上的问题:
a.无脑加锁,无论是添加,删除,获取都加锁,并使用同一个锁对象,导致性能极其低下
b.HashTable添加是全局锁,有且仅有一个线程可以操作HashTable,导致性能极其低下
ConcurrentHashMap既安全又效率高
public class MyThread extends Thread {
// public static HashMap map = new HashMap<>();//是线程不安全的
// public static Hashtable map = new Hashtable<>();//是线程安全的
public static ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>();//是线程安全的
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
map.put(i, i);// 00 11 22 33 44 .. 9999 9999
}
System.out.println("1万次OK");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
//1.创建线程
MyThread mt = new MyThread();
mt.start();
for (int i = 10000; i < 20000; i++) {
MyThread.map.put(i, i);//10000 10000 ... 19999 19999
}
System.out.println("1万次OK");
Thread.sleep(3000);
System.out.println(MyThread.map.size());
}
}
输出结果:
1万次OK
1万次OK
20000(始终是20000!)
为什么Hashtable效率低而ConcurrentHashMap效率高?
public class MyThread extends Thread {
// public static Map map = new Hashtable<>();
public static Map<Integer, Integer> map = new ConcurrentHashMap<>();
@Override
public void run() {
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
map.put(i, i);
}
long end = System.currentTimeMillis();
System.out.println((end - start) + " 毫秒");
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
new MyThread().start();//开启1000个线程
}
Thread.sleep(1000 * 20);
// 由于每个线程执行时间稍长,所以这里多停顿一会
System.out.println("map的最终大小:" + MyThread.map.size());
}
}
输出结果:
Hashtable,每个线程7000-8000毫秒(我的电脑的数据)
ConcurrentHashMap,每个线程8-2000毫秒(我的电脑的数据)
可见:ConcurrentHashMap性能比HashTable高一大截!!
为什么ConcurrentHashMap性能更高,因为如下代码:
public V put(K key, V value) {
return putVal(key, value, false);
}
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
//先计算键的哈希值
int hash = spread(key.hashCode());
//根据键要添加的桶,对该桶进行加锁
synchronized (f) {
}
}
从上面代码我们可以看出,ConcurrentHashMap使用了局部桶锁+CAS机制,从而提高了性能
4. CountDownLatch
允许一个线程等另外一个线程结束之后在继续执行!!!
CountDownLatch的API
构造方法:
public CountDownLatch(int count);指定计数的线程
成员方法:
public void await();让当前线程等待
public void countDown();减少需要等待的线程数量
案例:
发射火箭:
a.计算空气阻力(线程1)
b.计算因高度不同的地球引力(线程2)
c.发射火箭(线程3)
CountDownLatch的使用案例
/**
* 计算空气阻力
*/
public class ThreadOne extends Thread {
public CountDownLatch latch;
public ThreadOne(CountDownLatch latch){
this.latch = latch;
}
@Override
public void run() {
try{
//模拟计算过程,随机休眠0-5秒
Thread.sleep(new Random().nextInt(5000));
}catch (Exception e){
e.printStackTrace();
}
System.out.println("空气阻力计算完了...");
latch.countDown();
}
}
/**
* 计算地球引力
*/
public class ThreadTwo extends Thread {
public CountDownLatch latch;
public ThreadTwo(CountDownLatch latch){
this.latch = latch;
}
@Override
public void run() {
try{
//模拟计算过程,随机休眠0-5秒
Thread.sleep(new Random().nextInt(5000));
}catch (Exception e){
e.printStackTrace();
}
System.out.println("地球引力计算完了...");
latch.countDown();
}
}
/**
* 火箭发射工作
*/
public class ThreadThree extends Thread {
public CountDownLatch latch;
public ThreadThree(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
//让当前线程等待
try {
latch.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("开始发射火箭...");
for (int i = 0; i < 100; i++) {
System.out.println("....");
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class TestDemo {
public static void main(String[] args) {
//0.创建
CountDownLatch latch = new CountDownLatch(2);
//1.创建三个线程
ThreadOne t1 = new ThreadOne(latch);
ThreadTwo t2 = new ThreadTwo(latch);
ThreadThree t3 = new ThreadThree(latch);
//2.启动线程
t1.start();
t2.start();
t3.start();
}
}
输出结果:
地球引力计算完了…
空气阻力计算完了…
开始发射火箭…
5. CyclicBarrier
让一组线程均到达某个屏障点.然后去执行某个任务!!!
CyclicBarrier的API
构造方法:
public CyclicBarrier(int parties, Runnable barrierAction);
参数1:parties表示这组线程的数量!
参数2:barrierAction 表示一组线程都到达之后需要执行的任务!
成员方法:
public int await(); 让当前线程阻塞
CyclicBarrier的使用案例
/**
* 等所有元婴老怪都到了,在去合力打开宝藏大门!!!
*/
public class PersonThread extends Thread {
private CyclicBarrier cb;
public PersonThread(String name, CyclicBarrier cb) {
super(name);
this.cb = cb;
}
@Override
public void run() {
try {
//模拟不同的人以不同的时间到达
Thread.sleep(new Random().nextInt(10000));
System.out.println(getName()+"到了...");
//调用CyclicBarrier的await方法
cb.await();
}catch (Exception e){
e.printStackTrace();
}
}
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException {
//0.创建一个
CyclicBarrier cb = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("打开宝藏大门....寻宝....");
}
});
//1.创建五个线程
PersonThread p1 = new PersonThread("韩立",cb);
PersonThread p2 = new PersonThread("红发老祖",cb);
PersonThread p3 = new PersonThread("血发老魔",cb);
PersonThread p4 = new PersonThread("白发老怪",cb);
PersonThread p5 = new PersonThread("黑发老铁",cb);
//2.启动
p1.start();
p2.start();
p3.start();
p4.start();
p5.start();
}
}
6. Semaphore
控制并发线程的数量!!
Semaphore的API
构造方法:
public Semaphore(int permits);
参数permits称为许可证,即最大的线程并发数量
成员方法:
public void acquire(); 表示获取许可证
public void release(); 释放许可证
Semaphore的使用案例
需求:设计案例保证最多只能有三个线程并发执行
public class MyThread extends Thread {
private Semaphore sm;
public MyThread(Semaphore sm) {
this.sm = sm;
}
@Override
public void run() {
try {
//获取许可证...
sm.acquire();
System.out.println("线程:" + Thread.currentThread().getName() + "开始了....");
//模拟耗时
//模拟不同的人以不同的时间到达
Thread.sleep(new Random().nextInt(5000));
System.out.println("线程:" + Thread.currentThread().getName() + "结束了....");
//释放许可证
sm.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class TestDemo {
public static void main(String[] args) {
//0.使用Semaphore
Semaphore sm = new Semaphore(2);
//1.循环创建
for (int i = 0; i < 10; i++) {
new MyThread(sm).start();
}
}
}
7. Exchanger
用于两个线程间做数据交换的
Exchanger的API
构造方法:
public Exchanger();
成员方法:
public V exchange(V x);//交换数据
Exchanger的使用案例
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();
}
}
运行结果:
线程A给线程B发信息…
线程B给线程A发信息…
同时获取到线程B的回信:BBBBBBBBBBBBBBBB
同时获取到线程A的回信:AAAAAAAAAAAA