JUC并发编程&常用辅助类

JUC并发编程

1.什么是JUC?

java.util.concurrent、java.util.concurrent.atomic、java.util.concurrent.locks 这三个操作线程的包包(简称JUC )。

并发编程的本质:充分利用CPU的资源!

2.Lock锁

锁是一种用于解决安全的机制,java中锁一般都是锁的对象,或者锁的是需要进行修改的属性或方法。

1) 传统:synchronized
public class SynchronizedDemo {
    public static void main(String[] args) {
        //多线程操作
        Ticket ticket = new Ticket();

        new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"A").start();
        new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"B").start();
        new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"C").start();
        new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"D").start();

    }
}

class Ticket{
    private static int number=50;

    //卖票方式
    public synchronized void sale(){
        if (number>0){
            System.out.println(Thread.currentThread().getName() + "购买了第" + (number--) + "张票,剩余票数为"+number);
        }
    }
}
2) Lock接口

Lock锁是一个接口,其所有的实现类有:

ReentrantLock(可重入锁)

ReentrantReadWriteLock.ReadLock(可重入读锁)

ReentrantReadWriteLock.WriteLock(可重入写锁)

/**
 * 公平锁:十分公平,必须先来后到~
 * 非公平锁:十分不公平,可以插队(默认为非公平锁).
 */
public class LockDemo {
    public static void main(String[] args) {
        //多线程操作
        Ticket2 ticket = new Ticket2();
        new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"A").start();
        new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"B").start();
        new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"C").start();
        new Thread(()-> { for (int i = 0; i < 60; i++) ticket.sale(); },"D").start();
    }
}

//资源类
class Ticket2{
    private static int number=50;

    Lock lock= new ReentrantLock(false);//设置true——公平锁,设置false-非公平锁,默认为非公平锁

    //卖票方式
    public void sale(){
        //加锁
        lock.lock();
        try {
            if (number>0){
                System.out.println(Thread.currentThread().getName() + "购买了第" + (number--) + "张票,剩余票数为"+number);
            }
        } finally {
            //解锁
            lock.unlock();
        }
    }
}

 synchronized锁与Lock锁的区别:

  • synchronized是内置的java关键字,而Lock是一个接口 。
  • synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁。
  • synchronized会自动释放锁,Lock必须要手动释放锁!否则会造成死锁.
  • synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻的等);Lock锁就不会一直等下
  • synchronized 可重入锁 不可以中断的 非公平,Lock 可重入锁 可以判断的 非公平(可以设置)
  • synchronized 适合锁少量同步代码,Lock适合锁大量同步代码.

3.生产者和消费者问题

1)synchronized版
public class TestProAndCon1 {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            try {
                for (int i = 0; i < 20; i++) {
                    data.Production();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        new Thread(()->{
            try {
                for (int i = 0; i < 20; i++) {
                    data.Consumption();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B").start();
    }
}

class Data {
    private int number = 0;

    //生产
    public synchronized void Production() throws InterruptedException {
        while (number != 0) {
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"线程生产+1,目前总数"+number);
        this.notifyAll();
    }

    //消费
    public synchronized void Consumption() throws InterruptedException {
        while (number==0){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"线程消费-1,目前总数"+number);
        this.notifyAll();
    }
}
2)JUC并发版
public class TestProAndCon2{
    public static void main(String[] args) {
        Data01 data01 = new Data01();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data01.printA();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data01.printB();
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data01.printC();
            }
        }, "C").start();


        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data01.printD();
            }
        }, "D").start();
    }
}

class Data01 {
    //可重入锁
    private final Lock lock = new ReentrantLock();
    //监视器
    private final Condition condition1 = lock.newCondition();
    private final Condition condition2 = lock.newCondition();
    private final Condition condition3 = lock.newCondition();
    private final Condition condition4 = lock.newCondition();
    //物品数量
    private int count = 0;

    //生产
    public void printA() {
        lock.lock();
        try {
            while (count != 0) {
                //等待
                condition1.await();
            }
            count++;
            System.out.println(Thread.currentThread().getName() + "生产物品数量+1物品还剩" + count + "个,通知B消费");
            //唤醒B
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    //消费
    public void printB() {
        lock.lock();
        try {
            while (count <= 0) {
                condition2.await();
            }
            count--;
            System.out.println(Thread.currentThread().getName() + "消费物品数量-1物品还剩" + count + "个,通知C生产");
            //通知C
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //生产
    public void printC() {
        try {
            lock.lock();
            while (count != 0) {
                //等待
                condition3.await();
            }
            count++;
            System.out.println(Thread.currentThread().getName() + "生产物品数量+1物品还剩" + count + "个,通知D消费");
            //唤醒D
            condition4.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //消费
    public void printD() {
        lock.lock();
        try {
            while (count <= 0) {
                condition4.await();
            }
            count--;
            System.out.println(Thread.currentThread().getName() + "消费物品数量-1物品还剩" + count + "个,通知A生产");
            //通知A
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

4.如何判断锁的是什么?

1)场景一:doOther方法执行的时候需要等待doSome方法的结束吗?
//doOther方法执行的时候需要等待doSome方法的结束吗?
//不需要,因为doOther方法没有使用synchronized修饰
public class Exam01 {
    public static void main(String[] args) {
        MyClass myClass=new MyClass();
        Thread t1=new MyThread(myClass);
        Thread t2=new MyThread(myClass);

        t1.setName("t1");
        t2.setName("t2");
        t1.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();

    }
}


class MyThread extends Thread{
    private MyClass mc;
    public MyThread(MyClass mc) {
        this.mc = mc;
    }
    public void run() {
        if(Thread.currentThread().getName().contentEquals("t1")) {
            mc.doSome();
        }
        if(Thread.currentThread().getName().contentEquals("t2")) {
            mc.doOther();
        }
    }
}

class MyClass{
    public synchronized void doSome() {
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }
    public void doOther() {
        System.out.println("doOther Begin");
        System.out.println("doOther Over");
    }
}
2)场景二:doOther方法执行的时候需要等待doSome方法的结束吗?
//doOther方法执行的时候需要等待doSome方法的结束吗?
//需要,加了synchronized,并且锁的是同一个对象 myClass
public class Exam02 {
    public static void main(String[] args) {

        MyClass myClass=new MyClass();
        Thread t1=new MyThread(myClass);
        Thread t2=new MyThread(myClass);

        t1.setName("t1");
        t2.setName("t2");
        t1.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}

class MyThread extends Thread{
    private MyClass mc;
    public MyThread(MyClass mc) {
        this.mc = mc;
    }
    public void run() {
        if(Thread.currentThread().getName().contentEquals("t1")) {
            mc.doSome();
        }
        if(Thread.currentThread().getName().contentEquals("t2")) {
            mc.doOther();
        }
    }
}
class MyClass{
    public synchronized void doSome() {
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }
    public synchronized void doOther() {
        System.out.println("doOther Begin");
        System.out.println("doOther Over");
    }
}
3)场景三:doOther方法执行的时候需要等待doSome方法的结束吗?
//doOther方法执行的时候需要等待doSome方法的结束吗?
//不需要,加了synchronized,但锁的是不同的对象 myClass1和myClass2
public class Exam03 {
    public static void main(String[] args) {
        MyClass myClass1=new MyClass();
        MyClass myClass2=new MyClass();
        Thread t1=new MyThread(myClass1);
        Thread t2=new MyThread(myClass2);

        t1.setName("t1");
        t2.setName("t2");
        t1.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}

class MyThread extends Thread{
    private MyClass mc;
    public MyThread(MyClass mc) {
        this.mc = mc;
    }
    public void run() {
        if(Thread.currentThread().getName().contentEquals("t1")) {
            mc.doSome();
        }
        if(Thread.currentThread().getName().contentEquals("t2")) {
            mc.doOther();
        }
    }
}

class MyClass{
    public synchronized void doSome() {
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }
    public synchronized void doOther() {
        System.out.println("doOther Begin");
        System.out.println("doOther Over");
    }
}
4)场景四:doOther方法执行的时候需要等待doSome方法的结束吗?
//doOther方法执行的时候需要等待doSome方法的结束吗?
//需要,静态方法上加synchronized 是类锁,不管你创建了几个对象,类锁只有1把
public class Exam04 {
    public static void main(String[] args) {
        MyClass mc1 = new MyClass();
        MyClass mc2 = new MyClass();
        Thread t1 = new MyThread(mc1);
        Thread t2 = new MyThread(mc2);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }//保证t1先执行
        t2.start();
    }
}

class MyThread extends Thread{
    private MyClass mc;
    public MyThread(MyClass mc) {
        this.mc = mc;
    }
    public void run() {
        if(Thread.currentThread().getName().contentEquals("t1")) {
            mc.doSome();
        }
        if(Thread.currentThread().getName().contentEquals("t2")) {
            mc.doOther();
        }
    }
}

class MyClass{
    public synchronized static void doSome() {
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }
    public synchronized static void doOther() {
        System.out.println("doOther Begin");
        System.out.println("doOther Over");
    }
}

5)场景五:doOther方法执行的时候需要等待doSome方法的结束吗?

//doOther方法执行的时候需要等待doSome方法的结束吗?
//不需要,因为一个锁的是Class类,一个锁的是对象
public class Exam05 {
    public static void main(String[] args) {
        MyClass mc1 = new MyClass();
        MyClass mc2 = new MyClass();
        Thread t1 = new MyThread(mc1);
        Thread t2 = new MyThread(mc2);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }//保证t1先执行
        t2.start();
    }
}

class MyThread extends Thread{
    private MyClass mc;
    public MyThread(MyClass mc) {
        this.mc = mc;
    }
    public void run() {
        if(Thread.currentThread().getName().contentEquals("t1")) {
            mc.doSome();
        }
        if(Thread.currentThread().getName().contentEquals("t2")) {
            mc.doOther();
        }
    }
}

class MyClass{
    public synchronized static void doSome() {
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }
    public static void doOther() {
        System.out.println("doOther Begin");
        System.out.println("doOther Over");
    }
}

小节:

  • 如果是非静态的synchronized锁的是调用它的对象!
  • 如果是静态的synchronized锁的是Class类模板! 

5.集合安全问题

1)List

在单线程中ArrayList是安全的,但在并发中ArrayList是不安全的。如下:多线程向ArrayList中插入数据。

public class TestList {
    public static void main(String[] args) {
        test1();
        //test2(); 
        //test3();
    }
    public static void test1(){
        List<String> list = new ArrayList<>();
        for (int i = 1; i <= 100; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 10));
                System.out.println(Thread.currentThread().getName()+"----"+list);
            }, String.valueOf(i)).start();
        }
    }
    public static void test2(){
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 1; i <= 100; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 10));
                System.out.println(Thread.currentThread().getName()+"----"+list);
            },String.valueOf(i)).start();
        }
    }
    public static void test3(){
        List<String> oldlist = new ArrayList<>();
        List<String> list= Collections.synchronizedList(oldlist);
        for (int i = 1; i <= 100; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 10));
                System.out.println(Thread.currentThread().getName()+"----"+list);
            }, String.valueOf(i)).start();
        }
    }
}

运行test1()方法,就出现了ConcurrentModificationException(并发修改异常)

解决方案:

  • 使用java并发包concurrent下的CopyOnWriteArrayList!见test2()方法。
  • 使用Collections.synchronizedList()方法将集合转换为安全的集合。见test3()方法。
2)Map

同样HashMap在并发情况下也是也是不安全的。

public class TestMap {
    public static void main(String[] args) {
        test1();
        //test2();
        //test3();
    }
    public static void test1(){
        HashMap<Object, Object> hashMap = new HashMap<>();
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                hashMap.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 10));
                System.out.println(Thread.currentThread().getName()+"----"+hashMap);
            }).start();
        }
    }
    public static void test2(){
        ConcurrentHashMap<Object, Object> hashMap = new ConcurrentHashMap<>();
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                hashMap.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 10));
                System.out.println(Thread.currentThread().getName()+"----"+hashMap);
            }).start();
        }
    }
    public static void test3(){
        HashMap<Object, Object> oldHashMap = new HashMap<>();
        Map<Object, Object> hashMap = Collections.synchronizedMap(oldHashMap);
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                hashMap.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 10));
                System.out.println(Thread.currentThread().getName()+"----"+hashMap);
            }).start();
        }
    }
}

运行test1()方法,也会出现了ConcurrentModificationException(并发修改异常)。

解决方案:

  • 使用java并发包concurrent下的ConcurrentHashMap类。见test2()方法。
  • 使用Collections.synchronizedMap()把Map转换为安全的集合。见test3()方法。
2)Set

同样HashSet在并发情况下也是也是不安全的。

public class TestSet {
    public static void main(String[] args) {
        test1();
        //test2();
        //test3();
    }
    //同理可得:ConcurrentModificationException(并发修改异常)
    public static void test1(){
        Set<String> set = new HashSet<>();
        for (int i = 1; i <= 100; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0, 10));
                System.out.println(Thread.currentThread().getName()+"----"+set);
            },String.valueOf(i)).start();
        }
    }
    public static void test2(){
        Set<String> set = new ConcurrentSkipListSet<>();
        for (int i = 1; i <= 100; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0, 10));
                System.out.println(Thread.currentThread().getName()+"----"+set);
            },String.valueOf(i)).start();
        }
    }
    public static void test3(){
        Set<String> oldSet = new HashSet<>();
        Set<String> set = Collections.synchronizedSet(oldSet);
        for (int i = 1; i <= 100; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0, 10));
                System.out.println(Thread.currentThread().getName()+"----"+set);
            },String.valueOf(i)).start();
        }
    }
}

运行test1()方法,也会出现了ConcurrentModificationException(并发修改异常)。

解决方案:

  • 使用java并发包concurrent下的ConcurrentSkipListSet类!见test()方法。
  • 使用Collections.synchronizedSet()方法将集合转换为安全的set集合!见test3()方法。

6.读写锁 ReadWriteLock

ReadWriteLock叫做读写锁是java.util.concurrent.locks包下的,它的写锁一次只被一个线程共享操作,读锁则可以被所有线程共享,所以又称为独占锁(写锁)和共享锁(读锁)。

public class TestReadWriteLock {
    public static void main(String[] args) {
        MyCacheLock myCache = new MyCacheLock();
        //MyCache myCache = new MyCache();
        //存写的线程
        for (int i = 0; i < 5; i++) {
            int temp=i;
            new Thread(()->{
                myCache .put(String.valueOf(temp),temp);
            },String.valueOf(i)).start();
        }

        //读写的线程
        for (int i = 0; i < 5; i++) {
            int temp=i;
            new Thread(()->{
                myCache.get(String.valueOf(temp));
            },String.valueOf(i)).start();
        }
    }
}

/**
 * 自定义缓存
 */
class MyCache{
    private volatile HashMap<String,Object> map=new HashMap<>();

    //存写
    public void put(String key,Object value){
        System.out.println(Thread.currentThread().getName()+"正在写入"+key+":"+value);
        map.put(key,value);
        System.out.println(Thread.currentThread().getName()+"写入完毕");
    }

    //读取
    public void get(String key){
        System.out.println(Thread.currentThread().getName()+"正在读取"+key);
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName()+"读取完毕");
    }
}


/**
 * 加锁的自定义缓存
 */
class MyCacheLock{
    private volatile HashMap<String,Object> map=new HashMap<>();
    //读写锁,更加细腻度的控制
    private final ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

    //存写 (存写的时候只希望有一个线程进行)
    public void put(String key,Object value){
        //上一把写锁
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"正在写入"+key+":"+value);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入完毕");
        } finally {
            lock.writeLock().unlock();
        }
    }

    //读取 (所有人都可以读)
    public void get(String key){
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"正在读取"+key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取完毕");
        } finally {
            lock.readLock().unlock();
        }
    }
}

小结:

独占锁(写锁):一次只能被一个线程占用 。

共享锁(读锁):多线程可以同时占有。

  • 读-读:可以共存! 
  • 读-写:不能共存!
  • 写-写:不能共存! 

7.volatile

volatile是java虚拟机提供的轻量级的同步机制。

  • 保证可见性
  • 不保证原子性
  • 禁止指令重排

保证可见性

public class TestJMM1 {

    //加入volatile可以保证程序的可见性
    private  static volatile boolean flag=true;

    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            while (flag){
                System.out.println("hello");
            }
        }).start();

        System.out.println("停顿!");
        TimeUnit.SECONDS.sleep(1);
        flag=false;
        System.out.println("修改完毕!");
    }
}

不保证原子性

public class TestJMM2 {

    //volatile不保证原子性
    private  volatile static int flag=0;

    public  static void add(){
        flag++;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+":"+flag);
    }
}

运行效果:

使用原子类

public class TestJMM3 {
    
    private volatile static AtomicInteger flag=new AtomicInteger();

    public static void add(){
        flag.getAndIncrement();//进行+1操作
    }
    public static  void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+":"+flag);
    }
}

8.阻塞队列 BlockingQueue

阻塞队列的特点:

  • 在队列为空的时候 ,获取元素的线程会等待队列变为非空。
  • 当队列满时 ,存储元素的线程会等待队列可用。

BlockingQueue的实现类有:

ArrayBlockingQueue : 一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue : 一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue : 一个支持优先级排序的无界阻塞队列。
DelayQueue: 一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue: 一个不存储元素的阻塞队列。
LinkedTransferQueue: 一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque: 一个由链表结构组成的双向阻塞队列。

使用队列

四组API:

方式        抛出异常有返回值,不抛出异常阻塞 等待超时等待
添加addofferput        offer(等待时间,等待单位)
移除removepolltakepoll(等待时间,等待单位)

检测队首元素

elementpeekpeekpeek

ArrayBlockingQueue -基于数组结构的阻塞队列 

public class TestBlockingQueue {
    public static void main(String[] args) throws Exception{
        //test1();
        //test2();
        //test3();
        test4();

    }
    //会抛出异常
    public static void test1(){
        ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3);
        System.out.println(objects.add("a"));
        System.out.println(objects.add("b"));
        System.out.println(objects.add("c"));
        //IllegalStateException: Queue full(队列已满异常!)
        //System.out.println(objects.add("d"));

        System.out.println("===================");

        System.out.println(objects.remove());
        System.out.println(objects.remove());
        System.out.println(objects.remove());
        //NoSuchElementException(队列为空异常!)
        //System.out.println(objects.remove());
    }
    //有返回值.不抛出异常
    public static void test2(){
        ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3);
        System.out.println(objects.offer("a"));
        System.out.println(objects.offer("b"));
        System.out.println(objects.offer("c"));
        System.out.println(objects.offer("d"));//false
        System.out.println("===================");
        System.out.println(objects.poll());
        System.out.println(objects.poll());
        System.out.println(objects.poll());
        System.out.println(objects.poll());//null
    }
    //等待,阻塞(一直阻塞)
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        arrayBlockingQueue.put("a");
        arrayBlockingQueue.put("b");
        arrayBlockingQueue.put("c");
        //arrayBlockingQueue.put("d");//队列没有位置了则会等待

        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        //System.out.println(arrayBlockingQueue.take()); //队列没有元素了则会等待
    }
    //超时等待
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.offer("a"));
        System.out.println(arrayBlockingQueue.offer("b"));
        System.out.println(arrayBlockingQueue.offer("c"));
        System.out.println(arrayBlockingQueue.offer("d", 2, TimeUnit.SECONDS));//等待超过两秒就退出
        System.out.println(arrayBlockingQueue.element());
        System.out.println(arrayBlockingQueue.peek());
        System.out.println("===================");
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll(2, TimeUnit.SECONDS));//等待超过两秒就退出
        //System.out.println(arrayBlockingQueue.element());  //检测队首元素,没有抛出异常
        System.out.println(arrayBlockingQueue.peek());  //检测队首元素,没有返回null
    }
}

SynchronousQueue 

特点: 没有容量,当进去一个元素必须等待取出才能再次进入元素!

public class TestSynchronousQueue {
    public static void main(String[] args) throws InterruptedException {
        SynchronousQueue<Object> objects = new SynchronousQueue<>();
        new Thread(()->{
            try {
                objects.put("a");
                System.out.println(Thread.currentThread().getName()+"put a");
                objects.put("b");
                System.out.println(Thread.currentThread().getName()+"put b");
                objects.put("c");
                System.out.println(Thread.currentThread().getName()+"put c");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T1").start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"->"+objects.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"->"+objects.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"->"+objects.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T2").start();
    }
}

常用辅助类

1.CountDownLatch类

使一个线程等待其他线程各自执行完毕后再执行。

常用方法:

1)countDownLatch.countDown();//表示数量-1

2)countDownLatch.await();  //等待计数器归零在往下执行!

当每次有线程调用 countDown()方法则数量减一,假设数量变为0,await()方法就会被唤醒继续执行。

//计数器
public class TestCountDownLatch {
    public static void main(String[] args) throws InterruptedException {
        //总数是6
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName()+"走了");
                countDownLatch.countDown();//表示数量-1
            },String.valueOf(i)).start();
        }
        //等待计数器归零在往下执行!
        countDownLatch.await();

        System.out.println("关门!");
    }
}

2.CyclicBarrier类

用于对多个线程任务进行同步执行。

主要方法:await 在所有线程任务都到达之前,线程任务都是阻塞状态。

public class TestCyclicBarrier {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("选手已准备就位!");
        });

        for (int i=1;i<=7;i++){
            int atI = i;
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+" " + atI +"号选手准备好了!");
                    cyclicBarrier.await(); //加法计数 等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },"线程"+i).start();
        }
    }
}

3.Semaphore类

信号量,在信号量定义两种操作acquire和release。

 常用方法:

1)semaphore.acquire(); //获得

2)semaphore.release(); //释放

当一个线程调用acquire操作,它通过成功获取信号量(信号量-1),有阻塞,直到有线程释放信号量,或者超时。调用release操作,实际上将信号量的值+1,然后唤醒等待的线程。可以实现多个共享资源的互斥使用,并发限流,控制最大线程数。

/**
 * Semaphore semaphore = new Semaphore(3);
 * 设置限流数量
 */
public class TestSemaphore {
    public static void main(String[] args) {
        //线程数量,停车位,限流的时候会用
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 8; i++) {
            new Thread(()->{
                //acquire() 得到
                //release() 释放
                try {
                    //得到车位
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"得到车位!");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"停车两秒后离开车位!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //离开车位
                    semaphore.release();
                }
            }).start();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值