声明:该专栏本人重新过一遍java知识点时候的笔记汇总,主要是每天的知识点+题解,算是让自己巩固复习,也希望能给初学的朋友们一点帮助,大佬们不喜勿喷(抱拳了老铁!)
往期回顾
Java学习day24:线程的同步和锁(例题+知识点详解)-CSDN博客
Java学习day25:守护线程、死锁、线程生命周期
一、守护线程
守护线程是用来守护非守护线程的。
那么什么是非守护线程?
非守护线程:
就是平常写的线程,main 主函数是非守护线程
一个应用程序中必须至少一个非守护线程
非守护线程一旦结束,守护线程就会自动消亡
古代皇帝(非守护线程),古代皇帝的妃子(守护线程),一旦皇帝死了,妃子要陪葬!!!
守护线程依附非守护线程,如果非守护线程消亡,那么守护线程随之消亡。
示例:
class MyThread1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("子线程正在执行" + i);
}
}
}
public class Demo1 {
public static void main(String[] args) {//在非守护线程中执行的
//启动线程
MyThread1 myThread1 = new MyThread1();
Thread thread = new Thread(myThread1);
//可以将myThread1设置为守护线程。
//就会在非守护线程结束De一瞬间,守护线程也会停止执行
thread.setDaemon(true);//变成了守护线程了
thread.start();
for (int i = 0; i < 200; i++) {
System.out.println("主线程正在执行" + i);
}
//在jvm中 main主线程, 还有一个线程 垃圾回收线程
//等到main主线程和垃圾回收线程 都结束De时候才守护线程才消亡!!!
}
}
真实开发的运用地方:
后台记录操作日志,监控内存,垃圾回收等 都可以使用守护线程。
二、死锁
1.什么是死锁
*死锁*是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于*死锁*状态或系统产生了*死锁*,这些永远在互相等待的进程称为*死锁*线程。
死锁是一种状态,当两个线程互相持有对方的资源的时候,却又不主动释放这个资源的时候。会导致死锁。这两个线程就会僵持住。代码就无法继续执行。
示例1:
你的钥匙丢完了。进不了门?咋办?找开锁公司。
开锁公司说你得给我提供你的身份证原件。我才能给你开锁
你说你得先开门,我的身份证原件在屋里锁着呢。
结果你们两个僵持不下,导致开门这个事推进不了
示例2:
线程A 有锁A
线程B 锁B
线程A会等待锁B 的释放
线程B会等待锁A的释放
开发中禁止使用死锁。
2.面试常考
2.1应用场景
并发场景,多线程。线程之间互不相让。在这几种情况下就得借助锁。
加锁的目的是为了线程安全,但是物极必反。尤其是加了锁以后。很容易出现死锁
2.2手写一个死锁
线程1 有锁1
线程2 锁2
线程1会等待锁2 的释放
线程2会等待锁1的释放
参考:
class DeadLock implements Runnable {
private boolean flag;//标记属性
private Object obj1;//锁住的对象
private Object obj2;//锁住的对象
public DeadLock(boolean flag, Object obj1, Object obj2) {
this.flag = flag;
this.obj1 = obj1;
this.obj2 = obj2;
}
@Override
public void run() {
//true
if (flag) {//如果设置为true,就让线程1进入到if语句中
synchronized (obj1) {//锁住的是obj1对象
//线程1持有obj1锁
System.out.println(Thread.currentThread().getName() + "拿到了锁1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("等待锁2的释......");
//我想在线程1中去使用线程2中的那个锁2 obj2
//线程1里面想用obj2锁对象
//也走不下去了
//线程1也没有释放obj1
synchronized (obj2) {
System.out.println("123");
System.out.println(Thread.currentThread().getName() + "拿到了锁1");
}
}
}
if (!flag) {//如果设置为false,就让线程2进入到if语句中
synchronized (obj2) {//锁住的是obj2对象
//线程2持有obj2这个锁
System.out.println(Thread.currentThread().getName() + "拿到了锁2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("等待锁1的释......");
//只有obj1释放掉以后,才能在线程2中对obj1加锁
//想一个问题,如果obj1锁对象没有被释放,那么下面这个代码
//线程2中去锁obj1
//在这等着呢 往下走不下去了 线程2没有释放obj2对象
synchronized (obj1) {
System.out.println("456");
System.out.println(Thread.currentThread().getName() + "拿到了锁1");
}
}
}
}
}
public class Demo1 {
public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = new Object();
//线程1可以进入到run方法中 if (flag)
DeadLock deadLock = new DeadLock(true, obj1, obj2);
new Thread(deadLock, "线程1").start();
//线程2 可以进入倒run方法中if(!flag)
DeadLock deadLock1 = new DeadLock(false, obj1, obj2);
new Thread(deadLock1, "线程2").start();
}
}
这段代码大家要足够熟悉!两个线程是怎么产生死锁的?线程1锁住了obj1对象的同时,还想去锁住obj2对象,而线程2在锁住了obj2对象的同时,又要去锁住obj1对象,但是线程1没有释放obj1对象,也就导致线程2拿不到obj1对象,也就没法释放obj2对象,线程1也就拿不到obj2对象,于是就产生了死锁。有点绕,大家要想清楚,能够自己独立写出来这段代码。
三、线程生命周期【面试】
1.五大过程
1)新建 | 当一个Thread类或其子类的对象被声明并创建时。新生的线程对象属于新建状态。 |
(2)就绪(可运行状态) | 处于新建状态的线程执行start()方法后,进入线程队列等待CPU时间片,该状态具备了运行的状态,只是没有分配到CPU资源。 |
(3)运行 | 当就绪的线程分配到CPU资源便进入运行状态,run()方法定义了线程的操作。 |
(4)阻塞 | 在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的的执行,进入阻塞状态。 |
(5)死亡 | 当线程执行完自己的操作或提前被强制性的终止或出现异常导致结束,会进入死亡状态。 |
注意:进入阻塞状态后,该线程就需要重新进入就绪状态进行等待获取cpu执行权。
以上,就是今天的所有知识点了。死锁问题,是Java知识点中的核心,很多问题都会涉及到,大家不仅要明白原理,还要能够自己手写死锁代码,需要大家得多花点时间,静下心看代码,写代码,多理解。
加油吧,预祝大家变得更强!