什么是多线程?
有了多线程,我们就可以让程序同时做多件事情
多线程的作用?
提高效率
多线程的应用场景?
只要你想让多个事情同时运行就需要用到多线程
比如:软件中的耗时操作、所有的聊天软件、所有的服务器
并发:
在同一时刻,有多个指令在单个CPU上交替执行
并行:
在同一时刻,有多个指令在多个CPU上同时执行
多线程三种实现方式对比
优点缺点继承Thread类编程比较简单,可以直接使用Thread类中的方法可以扩展性较差不能再继承其他的类实现Runnable接口扩展性强,实现该接口的同时还以继承其他的类可编程相对复杂,不能直接使用Thread类中的方法实现callable接口
继承Thread类
//新建一个类
public class MyThread extends Thread{
@Override
public void run() {
//书写线程代码
for(int i=0;i<100;i++){
System.out.println(getName()+"Hello!");
}
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) {
/**
* 多线程的第一种启动方式:
* 1. 自己定义一个类继承Thread
* 2. 重写run方法
* 3. 创建子类的对象,并启动线程
*/
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
实现Runnable接口
//新建一个类
public class MyRun implements Runnable{
@Override
public void run() {
//书写线程代码
for(int i=0;i<100;i++){
//获取到当前线程的对象
// Thread thread = Thread.currentThread();
// System.out.println(thread.getName()+"hello!");
System.out.println(Thread.currentThread().getName()+"Hello!");
}
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) {
/**
* 多线程的第二种启动方式:
* 1. 自己定义一个类实现Runnable接口
* 2. 重写run方法
* 3. 创建自己的类的对象
* 4. 创建一个Thread类的对象,并开启线程
*/
//创建MyRun对象
//表示多线程要执行的任务
MyRun myRun = new MyRun();
Thread t1 = new Thread(myRun);
Thread t2 = new Thread(myRun);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
实现callable接口
//新建一个类
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
//求1~100之间的和
int sum=0;
for (int i = 1; i <= 100; i++) {
sum=sum+i;
}
return sum;
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
/**
* 多线程的第三种启动方式:
* 特点:可以获取到多线程运行的结果
*
* 1. 创建一个类MyCallable实现Callable接口
* 2. 重写call方法(有返回值的,表示多线程运行的结果)
*
* 3. 创建MyCallable的对象(表示多线程要执行的任务)
* 4. 创建FutureTask的对象(作用管理多线程运行的结果)
* 5. 创建Thread类的对象,并启动(表示线程)
*/
//创建MyCallable对象(表示多线程要执行的任务)
MyCallable myCallable = new MyCallable();
//创建FutureTask的对象(作用管理多线程运行的结果)
FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
//创建线程对象
Thread thread = new Thread(futureTask);
//启动线程
thread.start();
//获取结果
Integer result = futureTask.get();
System.out.println(result);
}
}
常见的成员方法
方法名称 | 说明 |
---|---|
String getName() | 返回此线程的名称 |
void setName(String name) | 设置线程的名字(构造方法也可以设置名字) |
static Thread currentThread() | 获取当前线程的对象 |
static void sleep(long time) | 让线程休眠指定的时间,单位为毫秒 |
setPriority(int newPriority) | 设置线程的优先级 |
final int getPriority() | 获取线程的优先级 |
final void setDaemon(boolean on) | 设置为守护线程 |
public static void yield() | 出让线程/礼让线程 |
public static void join() | 插入线程/插队线程 |
设置、获取线程名称
//新建一个类
public class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
//书写线程代码
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "@" + i);
}
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
/**
* String getName() 返回此线程的名称
* void setName(String name) 设置线程的名字(构造方法也可以设置名字)
* 细节:
* 1、如果我们没有给线程设置名字,线程也是有默认的名字的
* 格式:Thread-X (X序号,从0开始的)
* 2、如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置
*
* static Thread currentThread() 获取当前线程的对象
* 细节:
* 当JVM虚拟机启动之后,会自动的启动多条线程
* 其中有一条线程就叫做main线程
* 他的作用就是去调用main方法,并执行里面的代码
* 在以前,我们写的所有的代码,其实都是运行在main线程当中
*
* static void sleep(long time) 让线程休眠指定的时间,单位为毫秒
* 细节:
* 1、哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
* 2、方法的参数:就表示睡眠的时间,单位毫秒
* 1秒=1000毫秒
* 3、当时间到了之后,线程会自动的醒来,继续执行下面的其他代码
*/
//1.创建线程的对象
MyThread t1 = new MyThread("飞机");
MyThread t2 = new MyThread("坦克");
//2.开启线程
t1.start();
t2.start();
//
// //哪条线程执行到这个方法,此时获取的就是哪条线程的对象
// Thread thread = Thread.currentThread();
// String name = thread.getName();
// System.out.println(name);
// System.out.println("111111");
// Thread.sleep(5000);
// System.out.println("222222");
}
}
//执行结果
飞机@0
坦克@0
飞机@1
坦克@1
飞机@2
坦克@2
坦克@3
飞机@3
飞机@4
坦克@4
飞机@5
坦克@5
坦克@6
飞机@6
飞机@7
坦克@7
飞机@8
坦克@8
飞机@9
坦克@9
线程优先级
setPriority(int newPriority)
优先级最大值为10,最小值为1,默认为5,设置线程的优先级,不要超过它的取值范围,不然会抛出异常。
优先级高的线程,会优先得到 CPU 的时间片的控制权。
垃圾回收器的线程的优先级非常低,所以即使调用了gc方法,也不一定能够得到立即执行。
获取当前线程、设置线程优先级
//新建一个类
public class MyRunnable implements Runnable {
@Override
public void run() {
//书写线程代码
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "----" + i);
}
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
/*
setPriority(int newPriority) 设置线程的优先级
final int getPriority() 获取线程的优先级
*/
MyRunnable mr = new MyRunnable();
//1.创建线程的对象
Thread t1 = new Thread(mr,"飞机");
Thread t2 = new Thread(mr,"坦克");
//2.设置优先级
t1.setPriority(1);//飞机优先级低
t2.setPriority(10);//坦克优先级高
//3.开启线程
t1.start();
t2.start();
}
}
//执行结果
坦克----1
坦克----2
坦克----3
坦克----4
坦克----5
飞机----1
坦克----6
坦克----7
坦克----8
坦克----9
坦克----10
飞机----2
飞机----3
飞机----4
飞机----5
飞机----6
飞机----7
飞机----8
飞机----9
飞机----10
守护线程
final void setDaemon(boolean on)
当一个线程结束,另一个线程也需要结束,被迫结束的线程就是守护线程。
比如QQ聊天算一个线程,在聊天的时候传输一个文件也是一个线程,但是当关闭QQ聊天的时候,聊天线程就结束了,传输线程也没有必要存在了,这个文件传输就是守护线程
设置守护线程
//新建一个类
public class MyThread1 extends Thread {
@Override
public void run() {
//书写线程代码
for (int i = 1; i <= 10; i++) {
System.out.println(getName() + "@" + i);
}
}
}
//新建一个类
public class MyThread2 extends Thread {
@Override
public void run() {
//书写线程代码
for (int i = 1; i <= 100; i++) {
System.out.println(getName() + "@" + i);
}
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
/*
final void setDaemon(boolean on) 设置为守护线程
细节:
当其他的非守护线程执行完毕之后,守扩线程会陆续结束
通俗易懂:
当聊天窗口线程结束了,那么文件传输也没有存在的必要了
*/
//1.创建线程的对象
MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();
//设置线程名
t1.setName("聊天窗口");
t2.setName("文件传输");
//把第二个线程设置为守护线程
t2.setDaemon(true);
//2.开启线程
t1.start();
t2.start();
}
}
//执行结果
聊天窗口@1
聊天窗口@2
聊天窗口@3
聊天窗口@4
聊天窗口@5
文件传输@1
聊天窗口@6
聊天窗口@7
聊天窗口@8
聊天窗口@9
聊天窗口@10
文件传输@2
文件传输@3
文件传输@4
礼让线程
public static void yield()
表示出让当前CPU的执行权
//新建一个类
public class MyThread extends Thread {
@Override
public void run() {
//书写线程代码
for (int i = 1; i <= 20; i++) {
System.out.println(getName() + "@" + i);
//表示出让当前CPU的执行权
Thread.yield();
}
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
/*
public static void yield() 出让线程/礼让线程
*/
//1.创建线程的对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
//设置线程名
t1.setName("飞机");
t2.setName("坦克");
//2.开启线程
t1.start();
t2.start();
}
}
//执行结果
飞机@1
坦克@1
飞机@2
坦克@2
飞机@3
飞机@4
坦克@3
飞机@5
坦克@4
飞机@6
坦克@5
飞机@7
.....
插入线程
public static void join()
表示把这个线程,插入到当前线程之前。
jion方法一定要写在start方法之后,不然没用
//新建一个类
public class MyThread extends Thread {
@Override
public void run() {
//书写线程代码
for (int i = 1; i <= 10; i++) {
System.out.println(getName() + "@" + i);
//表示出让当前CPU的执行权
Thread.yield();
}
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
/*
public static void join() 插入线程/插队线程
*/
//1.创建线程的对象
MyThread t = new MyThread();
t.setName("土豆");
t.start();
//表示把这个线程,插入到当前线程之前。
//t: 土豆
//当前线程: main线程
t.join();
//执行在main线程当中的
for (int i = 0; i < 10; i++) {
System.out.println("main线程" + i);
}
}
}
//执行结果
土豆@1
土豆@2
土豆@3
土豆@4
土豆@5
土豆@6
土豆@7
土豆@8
土豆@9
土豆@10
main线程0
main线程1
main线程2
main线程3
main线程4
main线程5
main线程6
main线程7
main线程8
main线程9
线程的生命周期
线程安全问题
线程执行时具有随机性,可能当前票数+1,还没打印出来,又被其他线程夺取执行权,票数又+1,所以当打印的时候,可能并不是当前线程自己的值
//新建一个类
public class MyThread extends Thread {
//表示这个类所有的对象,都共享ticket数据
static int ticket = 0;//0~99
@Override
public void run() {
while (true){
if (ticket<100){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(getName()+"正在卖第"+(ticket+1)+"张票");
}else {
break;
}
}
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
/*
需求:
电源院正在上映电源,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
*/
//创建线程的对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
//起名
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
//开启线程
t1.start();
t2.start();
t3.start();
//问题:票重复,票超卖
}
}
//执行结果,第3、5、9、12、15、17...张票都有重复卖的问题,最后101、102张票属于超卖
窗口2正在卖第3张票
窗口3正在卖第3张票
窗口1正在卖第3张票
窗口3正在卖第4张票
窗口1正在卖第6张票
窗口2正在卖第5张票
窗口2正在卖第9张票
窗口3正在卖第9张票
窗口1正在卖第9张票
窗口1正在卖第12张票
窗口3正在卖第12张票
窗口2正在卖第10张票
窗口2正在卖第15张票
窗口1正在卖第15张票
窗口3正在卖第15张票
窗口2正在卖第17张票
窗口1正在卖第17张票
.........
窗口1正在卖第99张票
窗口3正在卖第100张票
窗口2正在卖第99张票
窗口2正在卖第101张票
窗口3正在卖第102张票
窗口1正在卖第101张票
.........
同步代码块
把操作共享数据的代码锁起来
特点1:锁默认打开,有一个线程进去了,锁自动关闭
特点2:里面的代码全部执行完毕,线程出来,锁自动打开
synchronized(锁){
操作共享数据的代码
}
synchronized
//新建一个类
public class MyThread extends Thread {
//表示这个类所有的对象,都共享ticket数据
static int ticket = 0;//0~99
//锁对象,一定要是唯一的
static Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (MyThread.class){
if (ticket<100){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(getName()+"正在卖第"+ticket+"张票");
}else {
break;
}
}
}
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
/*
需求:
电源院正在上映电源,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
*/
//创建线程的对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
//起名
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
//开启线程
t1.start();
t2.start();
t3.start();
}
}
//执行结果
窗口1正在卖第1张票
窗口1正在卖第2张票
窗口1正在卖第3张票
窗口1正在卖第4张票
窗口1正在卖第5张票
窗口1正在卖第6张票
窗口1正在卖第7张票
窗口1正在卖第8张票
窗口2正在卖第9张票
窗口3正在卖第10张票
窗口2正在卖第11张票
窗口1正在卖第12张票
.......
窗口3正在卖第90张票
窗口2正在卖第91张票
窗口1正在卖第92张票
窗口2正在卖第93张票
窗口3正在卖第94张票
窗口2正在卖第95张票
窗口2正在卖第96张票
窗口1正在卖第97张票
窗口1正在卖第98张票
窗口1正在卖第99张票
窗口2正在卖第100张票
同步方法
就是把synchronized关键字加到方法上
格式:
修饰符 synchronized 返回值类型 方法名(方法参数){…}
特点1:同步方法是锁住方法里面所有的代码
特点2:锁对象不能自己指定
非静态:this
静态:当前类的字节码文件对象
//新建一个类
public class MyRunnable implements Runnable {
//表示这个类所有的对象,都共享ticket数据
int ticket = 0;//0~99
@Override
public void run() {
//1.循环
while (true) {
//2.同步代码块(同步方法)
if (method()) break;
}
}
//this
private synchronized boolean method() {
//3.判断共享数据是否到了末尾,如果到了末尾
if (ticket == 100) {
return true;
} else {
//4.判断共享数据是否到了末尾,如果没有到末尾
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
}
return false;
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
/*
需求:
电源院正在上映电源,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
*/
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
Thread t3 = new Thread(mr);
//起名
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
//开启线程
t1.start();
t2.start();
t3.start();
}
}
//执行结果
窗口1正在卖第1张票
窗口1正在卖第2张票
窗口1正在卖第3张票
窗口1正在卖第4张票
窗口1正在卖第5张票
.......
窗口3正在卖第95张票
窗口3正在卖第96张票
窗口3正在卖第97张票
窗口3正在卖第98张票
窗口3正在卖第99张票
窗口3正在卖第100张票
lock锁
虽然我们可以理解同步代码块和同步方法的锁对象问题,
但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
void lock():获得锁 手动上锁
void unlock():释放锁 手动释放锁
//新建一个类
public class MyThread extends Thread {
//表示这个类所有的对象,都共享ticket数据
static int ticket = 0;//0~99
static Lock lock = new ReentrantLock();
@Override
public void run() {
//1.循环
while (true) {
//2.同步代码块
lock.lock();
try {
if (ticket == 100) {
break;
} else {
Thread.sleep(10);
ticket++;
System.out.println(getName() + "正在卖第" + ticket + "张票");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
/*
需求:
电源院正在上映电源,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
*/
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
//起名
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
//开启线程
t1.start();
t2.start();
t3.start();
}
}
//执行结果
窗口1正在卖第1张票
窗口1正在卖第2张票
窗口1正在卖第3张票
窗口1正在卖第4张票
窗口1正在卖第5张票
.......
窗口3正在卖第95张票
窗口3正在卖第96张票
窗口3正在卖第97张票
窗口3正在卖第98张票
窗口3正在卖第99张票
窗口3正在卖第100张票
死锁
两个线程相互等待对方释放资源,就形成了死锁
//新建一个类
public class MyThread extends Thread {
static Object objA = new Object();
static Object objB = new Object();
@Override
public void run() {
//1.循环
while (true) {
if ("线程A".equals(getName())){
synchronized (objA){
System.out.println("线程A拿到了A锁,准备拿B锁");
synchronized (objB){
System.out.println("线程A拿到了B锁,顺利执行完一轮");
}
}
}else if ("线程B".equals(getName())){
synchronized (objB){
System.out.println("线程B拿到了B锁,准备拿A锁");
synchronized (objA){
System.out.println("线程B拿到了A锁,顺利执行完一轮");
}
}
}
}
}
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
//起名
t1.setName("线程A");
t2.setName("线程B");
//开启线程
t1.start();
t2.start();
}
}
//执行结果
线程A拿到了A锁,准备拿B锁
线程B拿到了B锁,准备拿A锁
生产者和消费者(等待唤醒机制)
生产者消费者模式是一个十分经典的多线程协作的模式
消费者(消费数据)(吃货)
- 判断桌上是否有食物
- 如果没有就等待
- 如果有就开吃
- 吃完之后,唤醒厨师继续做
生产者(生产数据)(厨师)
- 判断桌子上是否有食物
- 有:等待
- 没有:制作食物
- 把食物放在桌上
- 叫醒等待的消费者开吃
方法名称 | 说明 |
---|---|
void wait() | 当前线程等待,直到被其他线程唤醒 |
void notify() | 随机唤醒单个线程 |
void notifyAll() | 唤醒所有线程 |
等待唤醒机制(生产者实现)
生产者(厨师)
//新建一个类
public class Cook extends Thread {
@Override
public void run() {
/*
* 1.循环
* 2.同步代码块
* 3.判断共享数据是否到了末尾(到了末尾)
* 4.判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)
*/
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
//判断桌子上是否有食物
if (Desk.foodFlag == 1) {
//如果有,就等待
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//如果没有,就制作食物
System.out.println("厨师做了一碗面条");
//修改桌子上的食物状态
Desk.foodFlag = 1;
//叫醒等待的消费者开吃
Desk.lock.notifyAll();
}
}
}
}
}
}
消费者(吃货)
//新建一个类
public class Foodie extends Thread {
@Override
public void run() {
/*
* 1.循环
* 2.同步代码块
* 3.判断共享数据是否到了末尾(到了末尾)
* 4.判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)
*/
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
//先判断桌子上是否有面条
if (Desk.foodFlag == 0) {
//如果没有,就等待
try {
Desk.lock.wait();//让当前线程跟锁进行绑定
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//把吃的总数-1
Desk.count--;
//如果有,就开吃
System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗!!!");
//吃完之后,唤醒厨师继续做
Desk.lock.notifyAll();
//修改桌子的状态
Desk.foodFlag = 0;
}
}
}
}
}
}
锁对象(桌子)
//新建一个类
public class Desk {
/**
* 作用:控制生产者和消费者的执行
*/
//是否有面条 0:没有面条 1:有面条
public static int foodFlag = 0;
//总个数
public static int count = 10;
//锁对象
public static Object lock = new Object();
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) {
/**
* 需求:完成生产者和消费者(等待唤醒机制)的代码
* 实现线程轮流交替执行的效果
*/
//创建线程的对象
Cook c = new Cook();
Foodie f = new Foodie();
//给线程设置名字
c.setName("厨师");
f.setName("吃货");
//开启线程
c.start();
f.start();
}
}
//执行结果
厨师做了一碗面条
吃货在吃面条,还能再吃9碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃8碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃7碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃6碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃5碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃4碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃3碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃2碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃1碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃0碗!!!
等待唤醒机制(阻塞队列方式实现)
阻塞队列的继承结构
接口:
- Iterable
- Collection
- Queue
- BlockingQueue
实现类:
- ArrayBlockingQueue 底层是数组,有界
- LinkedBlockingQueue 底层是链表,无界,但不是真正的无界,最大值为int的最大值
生产者(厨师)
//新建一个类
public class Cook extends Thread {
//阻塞队列
ArrayBlockingQueue<String> queue;
public Cook(ArrayBlockingQueue<String> queue){
this.queue = queue;
}
@Override
public void run() {
while (true) {
//不断的把面条放到阻塞队列当中
try {
queue.put("面条");
System.out.println("厨师做了一碗面条");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费者(吃货)
//新建一个类
public class Foodie extends Thread {
//阻塞队列
ArrayBlockingQueue<String> queue;
public Foodie(ArrayBlockingQueue<String> queue){
this.queue = queue;
}
@Override
public void run() {
while (true) {
//不断的从阻塞队列当中获取面条
try {
String food = queue.take();
System.out.println(food);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
锁对象(桌子)
//新建一个类
public class Desk {
/**
* 作用:控制生产者和消费者的执行
*/
//是否有面条 0:没有面条 1:有面条
public static int foodFlag = 0;
//总个数
public static int count = 10;
//锁对象
public static Object lock = new Object();
}
//新建一个类
public class ThreadDemo {
public static void main(String[] args) {
/**
* 需求:利用阻塞队列完成生产者和消费者(等待唤醒机制)的代码
* 细节:
* 生产者和消费者必须使用同一个阻塞队列
*/
//1.创建阻塞队列的对象
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
//2.创建线程的对象,并把阻塞队列传递过去
Cook c = new Cook(queue);
Foodie f = new Foodie(queue);
//给线程设置名字
c.setName("厨师");
f.setName("吃货");
//开启线程
c.start();
f.start();
}
}
//执行结果
厨师做了一碗面条
面条
面条
厨师做了一碗面条
厨师做了一碗面条
面条
面条
厨师做了一碗面条
厨师做了一碗面条
面条
面条
厨师做了一碗面条
厨师做了一碗面条
面条
厨师做了一碗面条
面条
.......
线程的状态
新建状态(NEW)---------------------------------> 创建线程对象
就绪状态(RUNNABLE)------------------------> start方法
阻塞状态(BLOCKED)--------------------------> 无法获得锁对象对象
等待状态(WAITING)---------------------------> wait方法
计时等待(TIMED_WAITING)----------------> sleep方法
结束状态(TERMINATED)--------------------> 全部代码运行完毕