一、对之前内容简单复习
1、JUC介绍
(1)概念
- 是在java.util.concurrent(juc)
总体有三个包java.util.concurrent和java.util.concurrent.atomic以及java.util.concurrent.locks包。 - 理解其实就是java并发编程
2、回顾点内容
(1)多线程回顾(卖票程序)
- 回顾卖票程序(企业级的)
package cn.mldn.juc.Ticket;
/**
* 高内聚低耦合的类
*/
class Ticket {
private int ticket = 30;
public synchronized void setTicket() {
//1、判断
while (ticket > 0) {
//2、卖票
ticket --;
System.out.println(Thread.currentThread().getName() + "线程卖出一张票" + ";剩余" + ticket + "张票");
}
}
}
/**
* 企业级多线程(题目,3个卖票员卖出30张票
* 1、在高内聚低耦合的前提下,线程 操作 资源类
* 2、
*/
public class TicketTest {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread thread = new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.setTicket();
}
},"线程A");
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.setTicket();
}
},"线程B");
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.setTicket();
}
},"线程C");
thread.start();
thread1.start();
thread2.start();
}
}
- 分析之前存在的问题
我们的synchronized相当于一个表锁,把我们的整体都锁住,性能不好。我们进一步细致的控制的话,就要使用lock了。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 高内聚低耦合的类
*/
class Ticket {
private int ticket = 30;
private Lock lock = new ReentrantLock();
public void setTicket() {
lock.lock();
try {
//1、判断
if (ticket > 0) {
//2、卖票
ticket --;
System.out.println(Thread.currentThread().getName() + "线程卖出一张票" + ";剩余" + ticket + "张票");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
* 企业级多线程(题目,3个卖票员卖出30张票
* 1、在高内聚低耦合的前提下,线程 操作 资源类
* 2、
*/
public class TicketTest {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread thread = new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.setTicket();
}
},"线程A");
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.setTicket();
}
},"线程B");
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.setTicket();
}
},"线程C");
thread.start();
thread1.start();
thread2.start();
}
}
- 这里复习我们的多线程的状态
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,//新建
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,//就绪状态
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,//阻塞状态
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
(2)Lambda表达式
- 无参数
interface foo {
public void hello() ;
}
public class LambdaExpress {
public static void main(String[] args) {
foo foo = () -> {
System.out.println("hello");
};
foo.hello();
}
}
- 有参数
interface foo {
/*public void hello() ;*/
public int hello2(int a) ;
}
public class LambdaExpress {
public static void main(String[] args) {
foo foo = (int a) -> {
System.out.println("hello");
return a;
};
foo.hello2(1);
}
}
- 两个参数
@FunctionalInterface
interface foo {
/*public void hello() ;*/
public int hello2(int a,int b) ;
}
public class LambdaExpress {
public static void main(String[] args) {
foo foo = (int a,int b) -> {
System.out.println("hello");
return a+b;
};
foo.hello2(1,2);
}
}
- 有自己default定义的也可以
- 还有static方法
其实很简单,只有一个方法的接口就是函数式接口,此时就可以使用Lambda表达式。
(3)生产者消费者模式复习
-
生产者和消费者模型时线程间进行通信的原始模型,这个很重要,在很多地方都能应用起来的。
-
看第一种情况
public class ConsumerANDProduce {
//不管怎么样,一定要记住,涉及到了多线程,就会是线程操作资源类
public static void main(String[] args) {
AirConditioner airConditioner = new AirConditioner();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
airConditioner.increment();
}
},"线程A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
airConditioner.decrement();
}
},"线程B").start();
}
}
//资源类
class AirConditioner {
private int number = 0;
public synchronized void increment() {
//1、判断
if(number != 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2、干活
number++;
System.out.println("" + Thread.currentThread().getName() + "线程,完成生产");
//3、通知
this.notifyAll();
}
public synchronized void decrement() {
//1、判断
if(number == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2、干活
number--;
System.out.println("" + Thread.currentThread().getName() + "线程,完成消费");
//3、通知
this.notifyAll();
}
}
- 使用while进行判断(防止虚假唤醒)
当有多个生产者线程和多个消费者同时进行操作数据的时候,此时就可能会虚假唤醒其它的,就可以用while进行等待。{用while循环的话,可以判断后,当你判断的被打断后,会再次拉回来进行判断}
public synchronized void increment() {
//1、判断
while (number != 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2、干活
number++;
System.out.println("" + Thread.currentThread().getName() + "线程,完成生产" + number);
//3、通知
this.notifyAll();
}
public synchronized void decrement() {
//1、判断
while (number == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2、干活
number--;
System.out.println("" + Thread.currentThread().getName() + "线程,完成消费" + number);
//3、通知
this.notifyAll();
}
原理分析:当我们使用两个线程,一个去生成,一个去消费,此时number不管怎么样,一个生成,唤醒另外一个,此时不管怎么样都不会出现问题。但是当你多个线程的时候,就比如两个加线程A和B,两个减线程C和D,
假如如果A线程进去后,判断,如果被wait进行等待后,它在等待,当其他消费线程消费后,唤醒生产线程生产,如果两个生产线程都进行唤醒,哦豁了撒,两个线程都生产,不行(我们的目的是生成一个消费一个)。当用了while判断后,你判断了,如果下次被唤醒了,它要进行重新判断。这就是为什么能写while就防止虚假唤醒的原因了。
- 使用Lock锁实现多线程
//资源类
class AirConditioner {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() {
lock.lock();
try {
//1、判断
while (number != 0) {
try {
condition.await();
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2、干活
number++;
System.out.println("" + Thread.currentThread().getName() + "线程,完成生产" + number);
//3、通知
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
//1、判断
while (number == 0) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//2、干活
number--;
System.out.println("" + Thread.currentThread().getName() + "线程,完成消费" + number);
//3、通知
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
- 使用Lock实现精确打击(精确唤醒,比如A线程执行后,我要叫醒B线程)
注意标志位
class ShareResource {
int number = 1;
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition();
private void AA() {
lock.lock();
try {
//1、判断
while (number != 1) {
conditionA.await();
}
//2、干活
for (int i = 0; i < 5; i++) {
System.out.println("AA线程打印");
}
//3、通知
number = 2;//设置标志位
conditionB.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private void BB() {
lock.lock();
try {
//1、判断
while (number != 2) {
conditionA.await();
}
//2、干活
for (int i = 0; i < 10; i++) {
System.out.println("BB线程打印");
}
//3、通知
number = 3;//设置标志位
conditionC.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private void CC() {
lock.lock();
try {
//1、判断
while (number != 3) {
conditionA.await();
}
//2、干活
for (int i = 0; i < 5; i++) {
System.out.println("CC线程打印");
}
//3、通知
number = 3;//设置标志位
conditionA.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
(4)再次回顾一下Callable接口
- Callable与Runnable区别
- 实现我们的Callable
不能直接传Callable接口,而是要通过和Runnable接口与Callable有联系的某一个类和方法。
它的实现类FutureTask【这里就是我们的多态的实现案例了】
- 获取我们返回值
- Callable细节理解
- 其实内部就是另外一个线程在工作,它进行处理的是,根据简单的先处理,难的留在后续处理。
- get方法一般放在最后一行 ,因为只要你调用这个get了,程序就认为你造成等待的时间,会被阻塞起来,会影响性能。
- 不管启动多少个线程,都是处理的同一个对象
它内部会缓存。
3、多线程锁(8锁问题)
1)第一锁
(1)现象
class Phone {
public synchronized void wangzry() throws Exception {
System.out.println("打开王者荣耀");
}
public synchronized void juedqs() throws Exception {
System.out.println("打开绝地求生");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
//1、资源类
Phone phone = new Phone();
new Thread(() -> {
try {
phone.wangzry();
} catch (Exception e) {
e.printStackTrace();
}
},"线程A").start();
Thread.sleep(1000);
new Thread(() -> {
try {
phone.juedqs();
} catch (Exception e) {
e.printStackTrace();
}
},"线程B").start();
}
}
先启动哪个方法
(2)原理
并不是A和B同时进入到了资源类里面,线程启动并不是按着我们的意愿来启动的,底层用了native定义的。我们强制性的要求A线程先启动(因为B被停了),所以上面个方法肯定要先被启动。
- 原理:synchronized锁的是当前资源类,只要用synchronized定义的普通方法,整个类被锁住,简单理解起来,就是你new的对象,不可能有两个及以上的线程访问。
2)第二锁
(1)现象
class Phone {
public synchronized void wangzry() throws Exception {
Thread.sleep(4000);
System.out.println("打开王者荣耀");
}
public synchronized void juedqs() throws Exception {
System.out.println("打开绝地求生");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
//1、资源类
Phone phone = new Phone();
new Thread(() -> {
try {
phone.wangzry();
} catch (Exception e) {
e.printStackTrace();
}
},"线程A").start();
new Thread(() -> {
try {
phone.juedqs();
} catch (Exception e) {
e.printStackTrace();
}
},"线程B").start();
}
}
(2)原理
- 同样的为什么我们的juedqs没有被暂停时间也会在后面等呢,原因和上面的 一样,同一时刻,有且仅有一个线程能访问资源类的当前对象,所以我们A线程在访问了,那你B线程也得等。
(3)第一和第二锁总结
在方法上加了synchronized锁的是对象,即对象锁,同一时刻有且仅有一个线程能访问。
3)第三锁
(1)现象
package cn.mldn.juc.cap;
class Phone {
public synchronized void wangzry() throws Exception {
Thread.sleep(4000);
System.out.println("打开王者荣耀");
}
public synchronized void juedqs() throws Exception {
System.out.println("打开绝地求生");
}
public void sayHello() throws Exception {
System.out.println("打开sayHello");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
//1、资源类
Phone phone = new Phone();
new Thread(() -> {
try {
phone.wangzry();
} catch (Exception e) {
e.printStackTrace();
}
},"线程A").start();
new Thread(() -> {
try {
phone.juedqs();
} catch (Exception e) {
e.printStackTrace();
}
},"线程B").start();
new Thread(() -> {
try {
phone.sayHello();
} catch (Exception e) {
e.printStackTrace();
}
},"线程C").start();
}
}
(2)原理
其实很好理解,普通方法(没有加上synchronized的方法),是不会和锁扯上关系的。
4)第四锁
(1)现象
package cn.mldn.juc.cap;
class Phone {
public synchronized void wangzry() throws Exception {
Thread.sleep(4000);
System.out.println("打开王者荣耀");
}
public synchronized void juedqs() throws Exception {
System.out.println("打开绝地求生");
}
public void sayHello() throws Exception {
System.out.println("打开sayHello");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
//1、资源类
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.wangzry();
} catch (Exception e) {
e.printStackTrace();
}
},"线程A").start();
new Thread(() -> {
try {
phone1.juedqs();
} catch (Exception e) {
e.printStackTrace();
}
},"线程B").start();
}
}
(2)原理
他们之间没有任何竞争关系,不存在关系了。所以肯定是这个答案了。
5)第五锁
(1)现象
class Phone {
public static synchronized void wangzry() throws Exception {
Thread.sleep(4000);
System.out.println("打开王者荣耀");
}
public static synchronized void juedqs() throws Exception {
System.out.println("打开绝地求生");
}
public void sayHello() throws Exception {
System.out.println("打开sayHello");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
//1、资源类
Phone phone = new Phone();
new Thread(() -> {
try {
phone.wangzry();
} catch (Exception e) {
e.printStackTrace();
}
},"线程A").start();
new Thread(() -> {
try {
phone.juedqs();
} catch (Exception e) {
e.printStackTrace();
}
},"线程B").start();
}
}
(2)原理
- 原理:static拥有部分,是属于这个类的,是这个类的模板。
- 加了static后,使用了我们的synchronized的锁后,它锁的是类型,结合第六锁来看,好理解。
6)第六锁
(1)现象
class Phone {
public static synchronized void wangzry() throws Exception {
Thread.sleep(4000);
System.out.println("打开王者荣耀");
}
public static synchronized void juedqs() throws Exception {
System.out.println("打开绝地求生");
}
public void sayHello() throws Exception {
System.out.println("打开sayHello");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
//1、资源类
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.wangzry();
} catch (Exception e) {
e.printStackTrace();
}
},"线程A").start();
Thread.sleep(1000);
new Thread(() -> {
try {
phone1.juedqs();
} catch (Exception e) {
e.printStackTrace();
}
},"线程B").start();
}
}
(2)原理
- 我们的phone1去调用的是juedqs,static把定义的锁方法,此时整个类模板都锁住了,所以,就算你上面个王者荣耀先启动都不管我是,乖乖的等我线程A执行完了,你B线程再执行。
(3)五和六锁的总结
我们用static后,锁的不再是一个个的对象,而是锁的整个Phone.class类型。
7)第七锁
(1)现象
package cn.mldn.juc.cap;
class Phone {
public static synchronized void wangzry() throws Exception {
Thread.sleep(4000);
System.out.println("打开王者荣耀");
}
public synchronized void juedqs() throws Exception {
System.out.println("打开绝地求生");
}
public void sayHello() throws Exception {
System.out.println("打开sayHello");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
//1、资源类
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.wangzry();
} catch (Exception e) {
e.printStackTrace();
}
},"线程A").start();
new Thread(() -> {
try {
phone.juedqs();
} catch (Exception e) {
e.printStackTrace();
}
},"线程B").start();
new Thread(() -> {
try {
phone.sayHello();
} catch (Exception e) {
e.printStackTrace();
}
},"线程C").start();
}
}
(2)原理
- 好理解了吧,有了static后,我锁住了整个模板,此时只能我访问完了,你后面的才能访问。而我们的sayHello和juedqs方法就凭争取,那个先就哪个。
8)第八锁
(1)现象
package cn.mldn.juc.cap;
class Phone {
public static synchronized void wangzry() throws Exception {
Thread.sleep(4000);
System.out.println("打开王者荣耀");
}
public synchronized void juedqs() throws Exception {
System.out.println("打开绝地求生");
}
public void sayHello() throws Exception {
System.out.println("打开sayHello");
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
//1、资源类
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.wangzry();
} catch (Exception e) {
e.printStackTrace();
}
},"线程A").start();
new Thread(() -> {
try {
phone1.sayHello();
} catch (Exception e) {
e.printStackTrace();
}
},"线程C").start();
}
}
(2)原理
我们的两部手机,虽然你们是来自于同一个模板,但是我们两个间互相不影响。所以你该调调你的,不关我事。
二、几个问题
1、控制线程顺序(CountDownLatchDemo)
(1)代码演示
比如我们的七个同血晚上下晚自习后,要离开教室,其中有一个人是班长,班长必须是最后一个把灯管了走。
- 错误示范
public class test1 {
public static void main(String[] args) {
for (int i = 0; i < 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "离开");
},"" + i).start();
}
System.out.println(Thread.currentThread().getName() + "班长离开");
}
}
- 正确处理
public class test1 {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "离开");
countDownLatch.countDown();
},"" + i).start();
}
//班长要先卡着,只有上面的启动完了再走。
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "班长离开");
}
}
(2)原理
2、控制多个线程等待(CyclicBarrierDemo)
- 其实可以理解为要等10个人到齐了再开会。
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(10,() -> {
System.out.println("集合完毕,可以开会");
});
for (int i = 0; i < 10; i++) {
int finalI = i;
new Thread(() -> {
System.out.println(String.valueOf(finalI) + "员工到会");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},i + "").start();
}
}
3、控制多线程的并发数(SemaphoreDemo)
(1)代码演示
- 上面的demo中,都是多个线程去抢占同一个资源。现在要想模拟多对多,比如我们的七个车抢占4个车位【然后后面的三个要等前面的走了才能进去,而且只能一个一个进去】。
- demo演示
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);//模拟资源,有三个空车位
for (int i = 0; i < 6; i++) {
new Thread(() -> {
try {
semaphore.acquire();//占用空车位
System.out.println(Thread.currentThread().getName() + "线程,抢占到车位");
Thread.sleep(3);//占用3秒
System.out.println(Thread.currentThread().getName() + "线程,离开");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();//并且释放之前的空车位
}
},"" + i).start();
}
}
(2)原理
如果把资源设置为1,那就相当于使用synchronized。
4、ReadWriteLockDemo
(1)概念理解
-
如果还是对lock不了解,可以自己去找找资源了解了解。
-
对ReadWriteLock理解就是:读的时候不能锁,写的时候必须锁。
(2)手写一个缓存
class MyCache {
private volatile Map<String,Object> map = new HashMap<>();
public void put(String key,Object value) {
System.out.println("" + Thread.currentThread().getName() + "写入数据");
map.put(key,value);
System.out.println("" + Thread.currentThread().getName() + "写入成功");
}
public void get(String key) {
System.out.println("" + Thread.currentThread().getName() + "开始读取");
map.get(key);
System.out.println("" + Thread.currentThread().getName() + "读取成功");
}
}
(3)模拟问题
class MyCache {
private volatile Map<String,Object> map = new HashMap<>();
public void put(String key,Object value) {
System.out.println("" + Thread.currentThread().getName() + "写入数据");
map.put(key,value);
System.out.println("" + Thread.currentThread().getName() + "写入成功");
}
public void get(String key) {
System.out.println("" + Thread.currentThread().getName() + "开始读取");
map.get(key);
System.out.println("" + Thread.currentThread().getName() + "读取成功");
}
}
public class test2 {
public static void main(String[] args) {
MyCache myCache = new MyCache();
//五个写
for (int i = 0; i < 5; i++) {
int finalI = i;
new Thread(() -> {
myCache.put("" + finalI, finalI+ "");
},finalI + "").start();
}
//五个读
for (int i = 0; i < 5; i++) {
int finalI = i;
new Thread(() -> {
myCache.get(finalI + "");
},finalI + "").start();
}
}
}
- 执行结果
0写入数据
3写入数据
4写入数据
4写入成功
1写入数据
2写入数据
1写入成功
3写入成功
0写入成功
2写入成功
0开始读取
0读取成功
1开始读取
1读取成功
2开始读取
4开始读取
4读取成功
3开始读取
2读取成功
3读取成功
我们没法保证数据的完整性(即原子性)
(4)解决问题
class MyCache {
private volatile Map<String,Object> map = new HashMap<>();
private ReadWriteLock readWriteLock = null;
public void put(String key,Object value) {
readWriteLock.writeLock().lock();
try {
System.out.println("" + Thread.currentThread().getName() + "写入数据");
map.put(key,value);
System.out.println("" + Thread.currentThread().getName() + "写入成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
public void get(String key) {
readWriteLock.readLock().lock();
try {
System.out.println("" + Thread.currentThread().getName() + "开始读取");
map.get(key);
System.out.println("" + Thread.currentThread().getName() + "读取成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
5、阻塞队列(BlockingQueueDemo)
(1)阻塞队列
- 当队列是空的时候,从队列里面获取元素的操作将被阻塞,直到其他线程往队列里面添加了元素。
- 当队列是满的时候,往队列里面方法元素的操作将被阻塞,直到里面元素被消费了才会能添加。
可以比喻为海底捞火锅店的候客区。
(2)用处
(3)分析继承结构
(4)核心API与代码测试
-
核心API
-
添加代码测试
-
remove删除方法调用
-
element检查队列中队首
-
offer,往队列里面添加数据
-
poll取出数据
-
put方法
线程不会结束
-
take阻塞等待生产
等待生产
-
offer(e,timeout)
等待四秒