文章目录
- 🌺lock锁
- ⭐获得锁
- ⭐释放锁
- ✨注意
- 🏳️🌈代码实现
- 🎈细节
- 🌺死锁
- ⭐解决方法
- 🎄等待唤醒机制
- ⭐代码实现
- 🎈注意
- 🛸使用阻塞队列实现等待唤醒机制
- 🍔线程的六种状态
比如下面这一段代码
我们在上一篇文章中讲过,进程进入synchroized后,其他进程不能进入,每次只允许执行一个进程
那么我们要想自定义运行过程,比如手动打开或者手动关闭,那么就应该使用lock锁
🌺lock锁
⭐获得锁
void lock();
⭐释放锁
void unlock();
✨注意
lock是接口不能直接实例化(即不能创建对象)
,可以采用它的实现类ReentrantLock来实例化
构造方法
Lock lock=new ReentrantLock();
🏳️🌈代码实现
MyRunnable.java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyRunnable implements Runnable {
static int ticket = 0;
static Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
if (ticket == 100) {
break;
} else {
Thread.sleep(10);
ticket++;
System.out.println(Thread.currentThread().getName() + "在卖第" + ticket + "张票!!!");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
ThreadDemo.java
public class ThreadDemo {
public static void main(String[] args) {
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();
}
}
🎈细节
我们发现,虽然已经到了100张票,但是
程序没有停止运行
,这是为什么呢
线程一break了,但是没有运行unlock,导致二,三线程不能向下执行,所以程序不会停止运行
改进方法
加上finally就行了,因为程序一定会执行finally中的代码
🌺死锁
比如 线程A 等待 线程B 释放锁,线程B 等待 线程A 释放锁,这样子二者就会卡死,这就是死锁
⭐解决方法
就是以后写代码的时候,尽量不要把2个锁嵌套起来
🎄等待唤醒机制
我们可以这样子来形容这个机制
比如顾客(消费者)和厨师(生产者)之间的关系
⭐代码实现
🤖Foodie.java
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;
}
}
}
}
}
}
🤖Cook.java
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();
}
}
}
}
}
}
🤖Desk.java
public class Desk {
/*
* 作用:控制生产者和消费者的执行
*
* */
//是否有面条 0:没有面条 1:有面条
public static int foodFlag = 0;
//总个数
public static int count = 10;
//锁对象
public static Object lock = new Object();
}
🤖ThreadDemo.java
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();
}
}
🎈注意
1.继承Thread类的主要目的是为了能够重写Thread类中的run()方法
2.为了可以共用同一个资源,Desk类的对象中要加上static
3.锁对象是唯一的
🛸使用阻塞队列实现等待唤醒机制
创建阻塞队列的对象
ArrayBlockingQueue < String > queue = new ArrayBlockingQueue<>(1);
🤖Cook.java
import java.util.concurrent.ArrayBlockingQueue;
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();
}
}
}
}
🤖Foodie.java
import java.util.concurrent.ArrayBlockingQueue;
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();
}
}
}
}
🤖ThreadDemo.java
import java.util.concurrent.ArrayBlockingQueue;
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);
//3.开启线程
c.start();
f.start();
}
}
🍔线程的六种状态
如果大家有什么不明白的地方,请在评论区进行讨论