shutdownHook 死锁解决
最近碰到一个问题,通过脚本执行kill -15后,程序并没有退出,进程一直都在,最后被退出脚本的通过kill -9,杀死。导致数据完整性被破坏,程序再重启后不可用。通过排查认后发现是在执行shutdownHook时死锁程序死锁。
复现问题导致问题的代码,通过定位发现,程序在退出时卡住,线上代码敏感,写一个demo来复现:
12345678910111213141516171819public class Test { private static final Object lock = new Object(); public static void main(String... args) { Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { System.out.println("Locking"); synchronized ...
产生线程死锁的原因和处理方式
产生背景
简单的说:线程1 想要去拿一个由 线程2 持有的锁,由于synchronized 的锁是互斥锁,某一时刻只能被一个线程所持有,所以线程1 就拿不到锁。
死锁原因是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。原因如下:
因为系统资源不足。
进程运行推进的顺序不合适,这种产生的最多。
资源分配不当。
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。
因为多线程访问共享资源,由于访问的顺序不当所造成的,通常是一个线程锁定了一个资源A,而又想去锁定资源B;在另一个线程中,锁定了资源B,而又想去锁定资源A以完成自身的操作,两个线程都想得到对方的资源,而不愿释放自己的资源,造成两个线程都在等待,而无法执行的情况。
产生背景: 在多线程环境下,争抢同是争抢对方资源(锁)就会产生该问题,即产生死锁。
java 死锁产生的四个必要条件
互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
不 ...
竞态条件 racing condition
前言多个线程读时,线程是安全的。当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。我的理解,竞态条件就是一种情况。
代码实现假设有 A、B 两个线程,调用 add 方法分别传入 1 和 2,理想条件下结果应该是 3。现在出现了不安全的情况,有可能结果不对。add方法就是临界区count 就是同一资源。
12345678public class Counter { protected long count = 0; // 临界区 public void add(long value) { // 被竞争的资源 this.count = this.count + value; } }
其实这样一看,说白了,就是要严格控制线程的执行顺序,假设是按A、B的顺序执行来讲,B依赖于A先执行完成,B再执行结果才是正确的,中间不能出现问题,否则如果,中间交叉执行,就有可能发生了竞态条件。
总结如果某个资源会被多个线程竞争,要保证安全性的情况下,可以加上一些必要的同步措施 ...
多线程 如何停止一个线程
前言这看似一个完全没有意义的问题,但是如果你是从搜索引擎过来的话,那么说明你碰到过这个问题。线程执行完不就退出了,说停止有什么意义?当然有意义,意义在于,一般创建线程后,如果是一次性的线程,执行结束就可以了,不用管它。如果是一个一直需要保持运行,而需要在某一时刻才需要停止的线程,就需要关注线程是如何退出的。
退出方式
退出标志: 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
interrupt: 使用interrupt方法中断线程。
可以但不推荐: 不推荐使用 stop、suspend及resume 方法。stop 相当于电脑断电关机一样,是不安全的方法。
退出标志方式:使用一个标志不控制线程是否需要继续执行。常驻的业务线程当中一般都会写循环,如果不写循环,一句话能搞定的事或者需要调用才执行的事,就没必要再开线程来处理。stop方法已经过时,不推荐使用。
开启多线程时,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束。原理:只要循环终止了,线程也就终止了。
1234567891011121314151617public class S ...
多线程 生产者消费者模式
多生产消费者模式真正的开发环境下,不可能只有两条线程在跑,但是也有特殊情况,就是只需要两条线程来处理。比如一个线程生产,一个线程消费。这是一种线程协作,这种情场景下,生产者 和 消费者会操作同一个共享变量。看到这里的小伙伴应该是对线程的基本用法有一定了解,这里就直接开始明确几个概念
生产者
生产数据的线程,比如产生订单
消费者
处理生产者产生的订单
实际环境中,生产数据的速度要大于消费的速度,这个现象在很多场景中都存在。
共享变量
会被多个线程共同访问的变量
生产者、消费者模式本质是,通过严格控制两个线程的交替执行,来实现一个生产、一个消费的模式,数据存储在共享变量中。
可以再扩展一下,比如常用的MQ,也是一种生产者消费者模式,Producer 生产消费,Consumer消费消息。
主要资源12345678910111213141516171819202122232425262728293031323334/** * @author liukai * @since 2017/2/18. */public class Resource { private bool ...
简单说明 lock 锁和 Condition 的操作
作用:使当前线程进入等待状,并交换执行执,等待被交换的当前执行线程唤醒,才可以继续执行,如果不被唤醒?场景:多个线程操作同一个共享资源时使用。
Condition 是执行条件。类似传统技术中的 wait 的 notify 功能。Condition 是基于一个 lock 而存在。注意的是,Condition 的创建来自同一个 lock 对象,
Condition 也行 wait 也好,套路就是使用三个工具来完成三步套路。即,用两个线程,同时跑两个代码,并且用 while 不段的去读取一个条件,来判断自己是否应该唤醒对方。
步骤:
先lock住
通过 lock 拿到 condition。再进行操作如 await
然后多个线程开始 await、single注意 await 会释放锁。
1await()的作用是能够让其他线程访问竞争资源,所以挂起状态就是要释放竞争资源的锁。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585 ...
wait-notify 实现生产者消费者模式
wait 和 nofity 在线程中的搭配使用,其实就是生产者消费者的一种应用。
一、为什么要有生产者消费者模式在实际应使用多线程时,线程间的角色并不一完全一样的,有的线程负责生产数据,有的线程负责消费数据。所在就会有一种情况,就是: 生产者生产数据太快,消费者消费能力跟不上。
比较线程A 不断的new 对象,并将对象放到一个队列里,而线程B,不断的从队列里拿出数据进行逻辑操作。显然线程A new 对象这一操作会更快,如果一直持续下去内存有可能会被撑暴。
解决这个问题的思路之一就是:生产者-消费者模式
二、wait、notify 简单应用示例Wait 类12345678910111213141516171819202122232425262728293031323334public class SimpleWaitTest implements Runnable { private Object object; public SimpleWaitTest(Object object) { this.object = object; ...
juc10-线程中断interrupt
interrupt作用
1.对运行中的线程,仅设置了一个停止的标记,但程序照常运行。2.对阻塞中的线程,该线程会抛出InterruptedException异常。
interrupt方法用于中断线程。调用该方法的线程的状态为将被置为"中断"状态。interrupt方法只能打上一个停止标记(改变状态),不能终止一个正在运行的线程,还需要加入一个判断才停止线程。
interrupt方法的使用效果并不像 for+break 语句那样,马上就停止循环。调用interrupt方法是在当前线程中打了一个停止标志,并不是真的停止线程。
三个主要API1.interrupt() :中间当前线程,实际并不是马上执行;2.interrupted(): 获取前线程的interrupt状态,关置重置interrupt状态为false,即未打interrupt状态 ;3.isInterrupted(): 获取前线程的interrupt状态,不重置;
看个小例子,子线程中断自己
12345678910111213141516171819202122232425262728/** * 主动中断线 ...
juc08-守护线程
守护线程所谓守护线程可以理解为后台线程,用户线程理解为前台线程,那么后台线程依赖前台线程。当前台线程远行结束后,后台线程自动结束,不管后台线程是不是无限循环。守护线程地位比用户线程底,用户线程退出后,就会随JVM线程退出,而不管工作是否完成!!!
java中有两种线程:
用户线程(User Thread)
守护线程(Daemon Thread)比如GC垃圾回收线程,这个线程具有最低的优先级。
两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:
如果用户线程全部退出离开,只剩下守护线程,虚拟机就会退出。如果还有至少一个用户线程,那么虚拟机就不会退出。
别急大白话在下面。
守护线程有什么用守护线程的目的是守护其他线程、守护其他线程、守护其他线程!!!守护线程的目的就是:为其他线程服务的线程。所以守护线程需要有被守护的线程!!!
说这么多,跟普通线程到底有什么区别,不还是一个用户创建的线程?区别在于:
用户线程退出,守护线程立即结束这个和普通线程可不一样,一般情况下,普通线程在main线程结整后,依然会运行直到任务结束,可以自行实验。守护线程不一样,只要用户线程退出,就立马退出,不管 ...
juc07 创建线程
创建线程JAVA中线程被封装成Thread对象。JDK API中有对Thread的说明,连创建方式都有。自定义线程有两种方式:
继承Thread
实现Runable接口
从打印结果是否是不同线程运行来验证多线程执行。主线程代码在main方法中,自定义线程方法代码在run方法中。
两种创建方式的区别:
Thread 的代码是存在子类当中;
Runable方式的代码是的实现接口的子类当中,还避免了单继承的问题。
一、继承 Thread 方式这种方式最简单,需要三个步骤:
继承 Thread 类
重写 run 方法,run方法要看成一个入口点。
调用 start 方法来执行自定义线程
实现代码:
123456789101112131415161718192021public class TestThread extends Thread { //1.继承Thread @Override public void run() { //2.重写run方法 //super.run(); for (int i = 0; i < 100; i ...