多线程并发包

什么是并发包(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<Integer> 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<Integer> 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<Integer, Integer> 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<Integer, Integer> map = new HashMap<>();//是线程不安全的
//    public static Hashtable<Integer, Integer> 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<Integer, Integer> 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lemon20120331

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值