当前位置:网站首页>多线程涉及的其它知识(死锁(等待唤醒机制),内存可见性问题以及定时器)
多线程涉及的其它知识(死锁(等待唤醒机制),内存可见性问题以及定时器)
2022-08-05 00:50:00 【naoguaziteng】
目录
一.死锁
1.死锁现象
如果出现了同步嵌套,就会很容易产生死锁问题.是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象.简单来说死锁是 两个或者两个以上的线程,在抢占CPU的执行权的时候,都处于等待状态.
public class MyThread extends Thread {
private boolean flag;
public MyThread(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (LockUtils.objA) {
System.out.println("true线程进来了持有ObjA锁");
synchronized (LockUtils.objB) {
System.out.println("true线程进来了持有ObjB锁");
}
}
} else {
synchronized (LockUtils.objB) {
System.out.println("false线程进来了持有ObjB锁");
synchronized (LockUtils.objA) {
System.out.println("false线程进来了持有ObjA锁");
}
}
}
}
}
public interface LockUtils {
//定义了两把锁对象
Object objA = new Object();
Object objB = new Object();
}
public class MyTest {
public static void main(String[] args) throws InterruptedException {
//创建了两个线程
MyThread th1 = new MyThread(true);
MyThread th2 = new MyThread(false);
//开启线程
th1.start();
th2.start();
}
}
运行结果便是如此!你等我,我等你.害!孽缘啊!所以一定要注意切勿造成如此"孽缘"!!!!
为了避免死锁,就出现了一个机制:线程间的等待唤醒机制
2.线程间的等待唤醒机制
什么是等待唤醒机制?
这个机制就相当于,生产线程生产出了资源. 也就是有资源了,于是就等着,那不可能一直干等着吧,不通知消费线程,他又怎么知道生产出来了资源!通知后,消费线程便拿到资源,然后消费资源,直到资源没有了,就等着,也不可能干等着吖,得通知生产线程这里没有资源了,需要生产线程去生产.这个 生产线程与消费线程之间的等待与通知对方就比较形象的展示了等待唤醒机制!
生产线程生产出了资源,就会等待消费线程来使用,如果没有通知消费线程,那么消费线程也就会一直等待,因为消费线程并不知道已经有资源了!这样的相互等待就会形成死锁现象!
等待与唤醒方法:
- wait () 在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待。
- wait (long timeout) 在其他线程调用此对象的 notify () 方法或 notifyAll () 方法,或者超过指定的时间量前,导致当前线程等待。
- notify () 唤醒在此对象监视器上等待的单个线程,随机唤醒。
- notifyAll () 唤醒在此对象监视器上等待的所有线程。
注意: notify()需要在同步方法或同步块中调用,即在调用前,线程必须获得该对象的对象级别锁. 在调用wait()方法之前,线程必须获得该对象的对象级别锁.
等待唤醒机制的案例演示:
//共享资源
public class Buns {
public String name;
public int price;
//定义一个标记
public boolean flag = false; //false 表示没有资源,true表示有资源
}
//消费者线程
public class GetThread extends Thread {
private Buns buns;
public GetThread(Buns buns) {
this.buns = buns;
}
@Override
public void run() {
while (true) {
synchronized (buns) {
if (!buns.flag) {
//如果进来了,说明没有资源,没有资源便会等待
try {
buns.wait();
//线程一旦等待,就会释放锁,下次被唤醒,就从这里醒来,也就是从哪里等待,从哪里醒来
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没有进入if语句,就会消费资源
System.out.println(buns.name + "====" + buns.price);
//消费资源后,通知生产线程去生产
buns.flag = false;
buns.notify();
}
}
}
}
//生产者线程
public class SetThread extends Thread {
private Buns buns;
int i = 0;
public SetThread(Buns buns) {
this.buns = buns;
}
@Override
public void run() {
while (true) {
synchronized (buns) {
//生产线程,生产出了资源,通知消费线程,消费资源
if (buns.flag) {
//进来了,表示有资源
try {
//有资源便会开始等待
buns.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产了资源
if (i % 2 == 0) {
buns.name = "肉包子";
buns.price = 3;
} else {
buns.name = "素包子";
buns.price = 2;
}
//通知消费者线程
buns.flag = true; //修改标记
buns.notify(); //唤醒等待的线程
}
i++;
}
}
}
//测试
public class MyTest {
public static void main(String[] args) {
//生成者线程 SetThread
//消费者线程 GetThread
//资源 Bun 两个线程共享
Buns buns = new Buns();
SetThread th1 = new SetThread(buns);
GetThread th2 = new GetThread(buns);
th2.start();
th1.start();
}
}
补:Wait和Sleep的区别:
- 它们最大本质的区别是,Sleep()不释放同步锁,Wait()释放同步锁。
- 还有用法的上的不同是:Sleep(milliseconds)可以用时间指定来使他自动醒过来,如果时间不到,就只能调用Interreput()来强行打断;Wait()可以用Notify()直接唤起。
- 这两个方法来自不同的类分别是Thread和Object
- 最主要是Sleep方法没有释放锁,而Wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
二.内存可见性问题(volatile)
1.volatile 关键字
咱们直接开门见山了, volatile可以解决内存可见性问题. volatile 关键字:当多个线程进行操作共享数据时,可以保证内存中的数据可见。相较于 synchronized 是一种较为轻量级的同步策略。
想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的.关于java内存模型,在小编的这篇文章里有浅浅的介绍.
2.如何保证可见性?
对于可见性,Java提供了 volatile关键字来保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。另外,通过 synchronized也能够保证可见性, synchronized能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。
3.volatile与锁的不同:
volatile 变量,用来确保将变量的更新操作通知到其他线程。可以将 volatile 看做一个轻量级的锁,但是又与锁有些不同!对于多线程, 不是一种互斥关系. 并且 不能保证变量状态的“原子性操作”!
三.定时器的概述和使用
定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以 通过Timer和TimerTask类来实现 定义调度的功能。
1.Timer:
- public Timer() //创建定时器对象
- public void schedule(TimerTask task, long delay) //延迟delay秒后执行任务
- public void schedule(TimerTask task,long delay,long period) //重复执行任务,第一次等待delay秒执行,以后每隔period秒重复执行.
- public void schedule(TimerTask task, Date time) //指定特定时间开始执行任务
- public void schedule(TimerTask task, Date firstTime, long period) //指定特定时间开始执行任务,以后每隔period秒重复执行
2.TimerTask:定时任务
- public abstract void run() //制定任务
- public boolean cancel() //取消定时器
public class MyTest {
public static void main(String[] args) {
//定时器 Timer
Timer timer = new Timer();
MyTimerTask myTimerTask = new MyTimerTask(timer);
//定时器,2秒后只执行一次该任务。
timer.schedule(myTimerTask, 2000);
//定时器,重复执行任务。第一次等待2秒执行 ,以后每隔1秒重复执行。
timer.schedule(myTimerTask, 2000, 1000);
}
}
//定时任务
class MyTimerTask extends TimerTask {
private Timer timer;
public MyTimerTask(Timer timer) {
this.timer = timer;
}
@Override
public void run() {
System.out.println("砰~~~~爆炸了");
timer.cancel(); //取消定时器
}
}
(小编也在努力学习更多哟!以后再慢慢分享的啦!)
希望对友友们有所帮助!!!!
边栏推荐
- 2021年11月网络规划设计师上午题知识点(下)
- Memory Forensics Series 1
- 数仓4.0(三)------数据仓库系统
- [FreeRTOS] FreeRTOS and stm32 built-in stack occupancy
- Pytorch usage and tricks
- Software testing interview questions: test life cycle, the test process is divided into several stages, and the meaning of each stage and the method used?
- If capturable=False, state_steps should not be CUDA tensors
- ORA-00257
- node uses redis
- Software testing interview questions: the difference and connection between black box testing, white box testing, and unit testing, integration testing, system testing, and acceptance testing?
猜你喜欢
JVM类加载简介
码率vs.分辨率,哪一个更重要?
Bit rate vs. resolution, which one is more important?
2022杭电多校第三场 K题 Taxi
CNI (Container Network Plugin)
新唐NUC980使用记录:在用户应用中使用GPIO
sqlite--nested exception is org.apache.ibatis.exceptions.PersistenceException:
阶段性测试完成后,你进行缺陷分析了么?
If capturable=False, state_steps should not be CUDA tensors
PCIe Core Configuration
随机推荐
软件测试面试题:负载测试、容量测试、强度测试的区别?
Knowledge Points for Network Planning Designers' Morning Questions in November 2021 (Part 1)
Software testing interview questions: the difference and connection between black box testing, white box testing, and unit testing, integration testing, system testing, and acceptance testing?
If capturable=False, state_steps should not be CUDA tensors
FSAWS 的全球基础设施和网络
执掌图表
tiup update
2022杭电多校第一场 1004 Ball
Software test interview questions: BIOS, Fat, IDE, Sata, SCSI, Ntfs windows NT?
Introduction to JVM class loading
软件测试面试题:软件测试类型都有哪些?
新唐NUC980使用记录:在用户应用中使用GPIO
B站7月榜单丨飞瓜数据B站UP主排行榜发布!
JWT简单介绍
GCC: compile-time library path and runtime library path
Creative code confession
could not build server_names_hash, you should increase server_names_hash_bucket_size: 32
软件测试面试题:软件都有多少种分类?
"WEB Security Penetration Testing" (28) Burp Collaborator-dnslog out-band technology
After the staged testing is complete, have you performed defect analysis?