JUC学习入门

目录

  • 一、什么是JUC
  • 二、线程与进程
    • 并发和并行
  • 三、多线程
  • 四、传统Synchronized锁
  • 五、Lock锁
  • 六、Synchronized和Lock区别
  • 七、传统的生产者消费者问题、防止虚假唤醒
    • 生产者和消费者问题Synchronized
    • 生产者和消费者问题JUC
    • Condition实现精准通知唤醒
  • 八、八锁现象彻底理解锁
  • 九、集合类不安全
    • List(不安全)
    • Set(不安全)
    • Map(不安全)
      • ConcurrentHashMap原理(等待更新)
  • 十、Callable(多线程创建的第三种方式)
  • 十一、常用的辅助类
    • 11.1、CountDownLatch
    • 11.2、CyclicBarrier
    • 11.3、Semaphore

本教程内容来自「遇到狂神说」:视频地址

一、什么是JUC

业务:普通的线程代码Thread
Runnable没有返回值,效率相比入Callable相对较低!
“JUC是java.util.concurrent的简写。在jdk官方手册中可以看到juc相关的jar包有三个。专门用于多线程的开发。”

二、线程与进程

线程、进程,如果不能使用一句话说出来的技术 ,不扎实!

进程: 一个程序,qq.exe Music.exe 程序的集合;
一个进程往往可以包含多个线程,至少包含一个!
java默认有几个线程?2个 main、GC
线程: 比如打开一个Typort,开启了一个自动保存,你在写的时候他会创建一个线程专门去保存数据。
java中创建线程的方法:Thread、runnable、callable
Java是否可以直接开启线程?
当然不行,java是无法直接操控硬件都。

public synchronized void start() {
     if (threadStatus != 0)
         throw new IllegalThreadStateException();
     group.add(this);

     boolean started = false;
     try {
         start0();
         started = true;
     } finally {
         try {
             if (!started) {
                 group.threadStartFailed(this);
             }
         } catch (Throwable ignore) {
         }
     }
 }
 //本地方法。底层都是c++和c,java无法直接操作硬件
private native void start0();

并发和并行

并发编程:并发、并行
并发(多线程操作同一个资源)

  • CPU一核,模拟出来多条线程,天下武功唯快不破,快速交替
    并行(多个任务同时进行)
  • CPU多核,多条线程可以同时执行;线程池
//获取CPU密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());

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

三、多线程

线程有几个状态

public enum State {
    //新生
    NEW,
    //运行
    RUNNABLE,
    //阻塞
    BLOCKED,
    //等待
    WAITING,
    //超时等待
    TIMED_WAITING,
    //终止
    TERMINATED;
}

wait/sleep区别

1、来自不同的类
wait=>Object
sleep=>Thread
2、关于锁的释放
wait会释放锁,sleep睡觉了,抱着锁睡觉,不会释放
3、使用范围不同的

  • wait 必须在同步代码块中
  • sleep 可以在任何地方睡
    4、释放需要捕获异常
  • wait 不需要捕获异常
  • sleep 必须捕获异常

四、传统Synchronized锁

package cn.qileyun;
/**
 * 真正的多线程开发,公式的开发,降低耦合性
 * 线程就是单独的资源类,没有任何附属操作!
 * 1、属性、方法
 */
public class SaleTicketDemo01 {
    public static void main(String[] args) {
        //并发:多线程操作同一个资源类lambok表达式 (次数)->{代码}
        Ticket ticket = new Ticket();
        //@FunctionlInterface 函数式接口
        new Thread(() -> {
            for (int i = 1; i < 60; i++) {
                ticket.sale();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 1; i < 60; i++) {
                ticket.sale();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 1; i < 60; i++) {
                ticket.sale();
            }
        }, "C").start();

    }
}

//资源类 OOP
class Ticket {
    //属性、方法
    private int number = 50;

    //卖票的方式
    //synchronized 本质:队列,锁
    public synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出类" + number + "票,剩余:" + number--);
        }
    }
}

五、Lock锁

JUC学习入门_第1张图片

公平锁:十分公平:可以先来后到
非公平锁:十分不公平:可以插队(默认)

  1. new ReentrantLock();
  2. lock 加锁
  3. unlock 释放锁
public class SaleTicketDemo02 {
    public static void main(String[] args) {
        //并发:多线程操作同一个资源类lambok表达式 (次数)->{代码}
        Ticket2 ticket = new Ticket2();
        //@FunctionlInterface 函数式接口
        new Thread(() -> {
            for (int i = 1; i < 60; i++) {
                ticket.sale();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 1; i < 60; i++) {
                ticket.sale();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 1; i < 60; i++) {
                ticket.sale();
            }
        }, "C").start();

    }
}

//Lock 三部曲
//1、 new ReentrantLock();
//2、lock 加锁
//3、unlock 释放锁
class Ticket2 {
    //属性、方法
    private int number = 50;

    Lock lock = new ReentrantLock();

    //卖票的方式
    //synchronized 本质:队列,锁
    public  void sale() {
        lock.lock();//加锁
        try {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出类" + number + "票,剩余:" + number--);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();//解锁
        }
    }
}

六、Synchronized和Lock区别

  1. Synchronized 内置的java关键字,Lock是一个java类
  2. Synchronized 无法判断获取锁的状态,Lock可以判断获取到了锁
  3. Synchronized 会自动释放锁,lock必须手动释放锁!如果不释放锁,死锁
  4. Synchronized 线程1(获取锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去
  5. Synchronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置);
  6. Synchronized 适合锁少量的代码同步问题,Lock适合大量的同步代码

锁是什么,如何判断锁的是谁?

七、传统的生产者消费者问题、防止虚假唤醒

面试题:单列模式、排序算法、生成者和消费者、死锁

生产者和消费者问题Synchronized

/**
 * 线程之间的腾讯问题,生产者和消费者问题! 等待唤醒,通知唤醒
 * 线程交替执行 A B操作同一个变量,num=0
 * A num+1
 * B num-1
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}

//等待,业务,通知
class  Data{//数字 资源类
    private int number = 0;
    //+1
    public synchronized  void increment() throws InterruptedException {
        if(number!=0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程,我+1 完毕了
        this.notifyAll();
    }
    //-1
    public synchronized void decrement() throws InterruptedException{
        if(number==0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程,我-1完成了 解除其他线程等待
        this.notifyAll();
    }
}
A=>1
B=>0
A=>1
...
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0

问题存在,A、B、C、D四个线程同时执行
就需要把if改成while 去循环判断

JUC学习入门_第2张图片

/**
 * 线程之间的腾讯问题,生产者和消费者问题! 等待唤醒,通知唤醒
 * 线程交替执行 A B操作同一个变量,num=0
 * A num+1
 * B num-1
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}

//等待,业务,通知
class  Data{//数字 资源类
    private int number = 0;
    //+1
    public synchronized  void increment() throws InterruptedException {
        while (number!=0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程,我+1 完毕了
        this.notifyAll();
    }
    //-1
    public synchronized void decrement() throws InterruptedException{
        while(number==0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程,我-1完成了 解除其他线程等待
        this.notifyAll();
    }
}

生产者和消费者问题JUC

传统的和JUC的区别
JUC学习入门_第3张图片
代码:

class  Data{//数字 资源类
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    //+1
    public   void increment() throws InterruptedException {
        lock.lock();

        try {
            while (number!=0){
                //等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知其他线程,我+1 完毕了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    //-1
    public  void decrement() throws InterruptedException{
        lock.lock();
        try {
            while (number==0){
                //等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知其他线程,我-1 完毕了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Condition实现精准通知唤醒

任何一个新的技术,绝对不是仅仅只覆盖原来的技术,优势和补充!

Condition精准的通知和唤醒线程

# 我们可以看到以下执行的顺序是随机的
A=>1
B=>0
A=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
D=>0
C=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0

代码测试:

/**
 * A 执行完调用B,B执行完调用C,C执行完执行A
 */
public class C {
    public static void main(String[] args) {
        DataC data = new DataC();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.printA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.printB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.printC();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();

    }
}

//等待,业务,通知
/**
 * A 执行完调用B,B执行完调用C,C执行完执行A
 */
public class C {
    public static void main(String[] args) {
        DataC data = new DataC();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.printA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.printB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.printC();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();

    }
}

//等待,业务,通知
class DataC {//数字 资源类
    private int number = 1;// 通过为1 A执行 2 B执行 3 C执行
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();


    public void printA() throws InterruptedException {
        lock.lock();
        try {
            while (number != 1) {
                //业务,判断->执行->通知
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>AAAAAA" );
            //唤醒指定的人 B
            number=2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public void printB() throws InterruptedException {
        lock.lock();
        try {
            while (number != 2) {
                //业务,判断->执行->通知
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>BBBBBB" );
            //唤醒C
            number=3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public void printC() throws InterruptedException {
        lock.lock();
        try {
            while (number != 3) {
                //业务,判断->执行->通知
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>CCCCCC" );
            //唤醒A
            number=1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    //生产线:下单->支付->交易->物流
}

JUC学习入门_第4张图片

八、八锁现象彻底理解锁

package cn.qileyun.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 7、一个静态的同步方法,1个普通方法,一个对象,先打印还是先打电话?打电话
 * 8、一个静态的同步方法,1个普通方法的同步方法,两个对象,先打印还是先打电话?打电话
 */
public class Test4 {
    public static void main(String[] args) {
        //两个对象的Class类模版只有一个,static
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        //锁的存在
        new Thread(() -> {
            phone1.sendSms();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }

}
//Phone3唯一的一个class对象
class Phone4{
    //静态的同步方法 锁的是Class模版
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    //普通方法 锁的是调用者
    public  synchronized void call() {
        System.out.println("打电话");
    }

}
package cn.qileyun.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 3、增加两一个普通方法!是先发短信还是hello? 普通方法
 * 4、两个对象。两个同步方法,发短信还是打电话?
 */
public class Test2 {
    public static void main(String[] args) {
        //两个对象,两个调用者,两把锁互不干扰
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();
        //锁的存在
        new Thread(() -> {
            phone1.sendSms();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }

}

class Phone2{
    //Synchronized 锁的对象是方法的调用者!
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }

    //这里没有锁 !不是同步方法
    public void hello(){
        System.out.println("hello");
    }
}

package cn.qileyun.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 5、增加两个静态的同步方法,只有一个对象,先打电话还是先发短信?发短信
 * 6、两个对象!增加两个静态的同步方法,先打印 发短信还是先打电话?发短信
 */
public class Test3 {
    public static void main(String[] args) {
        //两个对象的Class类模版只有一个,static 锁的是class
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();
        //锁的存在
        new Thread(() -> {
            phone1.sendSms();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }

}
//Phone3唯一的一个class对象
class Phone3{
    //Synchronized 锁的对象是方法的调用者!
    //static 静态方法
    //类一加载就有了!Class 模版  相当于锁的是Phone3.class
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call() {
        System.out.println("打电话");
    }

}

package cn.qileyun.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 7、一个静态的同步方法,1个普通方法,一个对象,先打印还是先打电话?打电话
 * 8、一个静态的同步方法,1个普通方法的同步方法,两个对象,先打印还是先打电话?打电话
 */
public class Test4 {
    public static void main(String[] args) {
        //两个对象的Class类模版只有一个,static
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        //锁的存在
        new Thread(() -> {
            phone1.sendSms();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }

}
//Phone3唯一的一个class对象
class Phone4{
    //静态的同步方法 锁的是Class模版
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    //普通方法 锁的是调用者
    public  synchronized void call() {
        System.out.println("打电话");
    }

}

小结:

  • new this 具体的一个手机
  • static Class 唯一的一个模版

九、集合类不安全

List(不安全)

解决方法:

  • 1、List list = new Vector<>();
  • 2、List list = Collections.synchronizedList(new ArrayList<>());
  • 3、List list = new CopyOnWriteArrayList<>();
public static void main(String[] args) {
     /** 并发下Arraylist 不安全的
      * List list = new ArrayList<>();
      * 1、List list = new Vector<>();
      * 2、List list = Collections.synchronizedList(new ArrayList<>());
      * 3、List list = new CopyOnWriteArrayList<>();
      */
     //CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略;
     // 多个线程调用的时候,list,读取的时候固定的,写入(覆盖)
     // 写入的时候避免覆盖,造成数据问题
     //CopyOnWriteArrayList 比Vector 优势在哪里?
     List<String> list = new CopyOnWriteArrayList<>();
     for (int i = 1; i <= 10; i++) {
         new Thread(() -> {
             list.add(UUID.randomUUID().toString().substring(0, 5));
             System.out.println(list);
         }, String.valueOf(i)).start();
     }
     list.forEach(System.out::println);
 }

CopyOnWriteArrayList添加原理
拷贝一份数组 去修改不会去影响读的操作

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);//数组长度+1 并且拷贝原有的数据
        newElements[len] = e;//把新数据添加到最后一个下标
        setArray(newElements);//设置成新的数组地址
        return true;
    } finally {
        lock.unlock();
    }
}

Set(不安全)

解决方法:

  • 1、Collections.synchronizedSet(new HashSet<>());
  • 2、new CopyOnWriteArraySet<>();
/**
 * 1、Collections.synchronizedSet(new HashSet<>());
 * 2、new CopyOnWriteArraySet<>();
 */
public class SetTest {
    public static void main(String[] args) {
//        Set set = new HashSet<>();

//        Set set = Collections.synchronizedSet(new HashSet<>());
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(set);//java.util.ConcurrentModificationException
            },String.valueOf(i)).start();
        }
    }
}

hashSet的本质就是一个HashMap

public HashSet() {
     map = new HashMap<>();
}
//add set本质就是map key是无法重复的! 相当于就是拿map的key做set的值,而且可以做到去重的效果
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object();//不变的值

Map(不安全)

JUC学习入门_第5张图片

/**
 * 1、Collections.synchronizedMap(new HashMap())
 * 2、new ConcurrentHashMap<>()
 */
public class MapTest {
    public static void main(String[] args) {
        //map 是这样用的吗? 不是,工作中不用HashMap
        //默认等价是什么?map = new HashMap(16,0.75f) 初始化容量,加载因子
        //Map map = new HashMap<>();//并发不安全
//        Map map = Collections.synchronizedMap(new HashMap());
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 0; i <= 30; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                System.out.println(map);//java.util.ConcurrentModificationException
            }, String.valueOf(i)).start();
        }
    }
}

ConcurrentHashMap原理(等待更新)

十、Callable(多线程创建的第三种方式)

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同,run()/ call()
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //new Thread(new MyThread()).start(); 传统创建线程方法

        MyThread myThread = new MyThread();
        //适配类
        FutureTask futureTask = new FutureTask(myThread);
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();//结果会被缓存

        //获取collable返回的结果
        String o = (String) futureTask.get();//这个get可能会造成堵塞,因为他需要等待线程执行完毕,可以竟可能的放在方法最后一行执行
        System.out.println(o);
    }
}

/**
 * Callable<需要返回的值类型>
 */
class MyThread implements Callable<String> {
    @Override
    public String call() {
        System.out.println("call()");//结果只会打印一边
        return "45678";
    }
}

十一、常用的辅助类

11.1、CountDownLatch

JUC学习入门_第6张图片

  • countDownLatch.countDown() 计数器-1
  • countDownLatch.await() 等待计数器归零,然后在向下执行
    每次线程调用countDown()数量-1.加入计数器变为-,await()就会被唤醒,继续执行
//计数器
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        //倒计时手机6,必须要执行完任务的时候,在使用
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"Go out");
                countDownLatch.countDown();//数量-1
            },String.valueOf(i)).start();
        }
        countDownLatch.await();//等待计数器归零,然后在向下执行
        System.out.println("Close Door");
    }
}

11.2、CyclicBarrier

加法计数器

  public static void main(String[] args) {
      /**
       * 集齐七棵龙珠召唤神龙
       */
      //召唤龙珠的线程
      CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
          System.out.println("召唤神龙成功!");
      });
		//必须执行完8条线程才能执行召唤神龙
      for (int i = 0; i < 7; i++) {
          //lambda能操作 到i吗
          final int temp = i;
          new Thread(()->{
              System.out.println(Thread.currentThread().getName()+"收集"+temp+"个龙珠");
              try {
                  cyclicBarrier.await();//等待
              } catch (InterruptedException e) {
                  e.printStackTrace();
              } catch (BrokenBarrierException e) {
                  e.printStackTrace();
              }
          }).start();
      }
  }

结果:

Thread-2收集2个龙珠
Thread-0收集0个龙珠
Thread-1收集1个龙珠
Thread-3收集3个龙珠
Thread-6收集6个龙珠
Thread-5收集5个龙珠
Thread-4收集4个龙珠
召唤神龙成功!

11.3、Semaphore

Semaphore:信号量
JUC学习入门_第7张图片
抢车位!
6车—3个停车位置
123
456

  • semaphore.acquire() 获得,假设如果已经满 了,等待被释放为止!
  • semaphore.release() 会将当前的信号量释放 +1,然后唤醒等待的线程
    作用:多个资源互斥的使用!并发限流,控制最大并发量!
public static void main(String[] args) {
    //线程数量: 停车位!限流!
    Semaphore semaphore = new Semaphore(3);
    for(int i =1;i<=6;i++){
        new Thread(()->{
            //acquice() 得到
            //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();//释放位置
            }
        },String.valueOf(i)).start();
    }
}

结果:

1抢到车位
3抢到车位
2抢到车位
3离开车位
1离开车位
4抢到车位
2离开车位
5抢到车位
6抢到车位
4离开车位
6离开车位
5离开车位

你可能感兴趣的:(Java,学习,java,面试)