死锁
什么是死锁?

如何分析
通过资源分配图分析,如果成环,就可能出现死锁

直接dump线程查看到底哪个线程出现问题,通过jstack 命令或者Java VisualVM这个可视化工具将 JVM 所有的线程栈信息导出来
解决办法
只有以下这四个条件都发生时才会出现死锁:
- 互斥:共享资源 X 和 Y 只能被一个线程占用;(锁)
- 占有且等待:线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
- 不可抢占:其他线程不能强行抢占线程 T1 占有的资源;
- 循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。
只要破坏其中一个,就可以成功避免死锁的发生。
破坏方案:
- 破坏“占用且等待”,我们可以一次性申请所有的资源,这样就不存在等待了。
- 破坏“不可抢占”,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源,这样不可抢占这个条件就破坏掉了。
- 破坏对于“循环等待”,可以靠按序申请资源来预防。
用面向对象思想写并发编程
封装共享变量
其中不变的变量用final关键字,可变的变量加锁实现原子操作
指定并发访问策略
避免共享
局部变量是线程安全的(利用线程的本地存储)
同步工具
synchronized,lock,读写锁,volatile等同步工具
线程
线程状态转换

TIMED_WAITING 和 WAITING 状态的区别,仅仅是触发条件多了超时参数。
并发包中的锁,都是基于LockSupport.park() 实现的。调用 LockSupport.park() 方法,当前线程会阻塞。调用 LockSupport.unpark(Thread thread) 可唤醒目标线程。

线程的使用
Thread常用方法
| 方法 | 描述 |
|---|---|
| start() | 启动一个线程 |
| yield() | 线程让步,让出CPU权,从运行状态进入到就绪状态,不会释放锁。 |
| sleep() | 线程休眠,从运行状态进入进入阻塞状态,但不会释放锁,休眠结束,会由阻塞状态变成就绪状态 |
| join() | 让子线程执行完,再执行父线程,由于底层是调用wait方法,所以会释放锁 |
| wait() | 让当前线程处于“等待(阻塞)状态”(当前线程指正在cpu上运行的线程),直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。 |
| wait(long timeout) | 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。 |
| notify() | 唤醒在此对象监视器上等待的单个线程。 |
| notifyAll() | 唤醒在此对象监视器上等待的所有线程。 |
| interrupt() | 中断线程。可以中断阻塞线程,不能中断运行线程 |
| boolean isInterrupted() | 检测对象的“中断标记”,并清除标记 |
| boolean interrupted() | //检测对象的“中断标记” |
创建线程
// 自定义线程对象
class MyThread extends Thread {
public void run() {
// 线程需要执行的代码
......
}
}
// 创建线程对象
MyThread myThread = new MyThread();
// 实现Runnable接口
class Runner implements Runnable {
@Override
public void run() {
// 线程需要执行的代码
......
}
}
// 创建线程对象
Thread thread = new Thread(new Runner());
启动线程
调用Thread的start()方法
中断线程
Java的中断是一种协作机制。也就是说调用线程对象的interrupt方法并不一定就中断了正在运行的线程,它只是要求线程自己在合适的时机中断自己。每个线程都有一个boolean的中断状态(这个状态不在Thread的属性上),interrupt方法仅仅只是将该状态置为true。
方法介绍
public void interrupt()//可以中断阻塞线程,不能中断运行线程(需要额外增加标记)
public boolean isInterrupted()//检测对象的“中断标记”,并清除标记
public static boolean interrupted() //检测对象的“中断标记”
中断阻塞状态线程
使用thread.interrupt()
class Example3 extends Thread {
public static void main(String args[]) throws Exception {
Example3 thread = new Example3();
System.out.println("Starting thread...");
thread.start();
Thread.sleep(3000);
System.out.println("Asking thread to stop...");
thread.interrupt();// 等中断信号量设置后再调用
Thread.sleep(3000);
System.out.println("Stopping application...");
}
public void run() {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Thread running...");
try {
Thread.sleep(1000);//线程阻塞,如果线程收到中断操作信号将抛出异常
} catch (InterruptedException e) {//从阻塞状态进入就绪状态
//抛出异常后,中断标示会被清除
Thread.currentThread().interrupt();//如果想中断线程,则需要重新设置中断位。
}
}
System.out.println("Thread exiting under request...");
}
}
注意:不可中断的操作,包括进入synchronized段以及Lock.lock(),inputSteam.read()等,调用interrupt()对于这几个问题无效,因为它们都不抛出中断异常。如果拿不到资源,它们会无限期阻塞下去。
中断非阻塞状态线程
1、使用thread.interrupt()
class Example2 extends Thread {
public static void main(String args[]) throws Exception {
Example2 thread = new Example2();
System.out.println("Starting thread...");
thread.start();
Thread.sleep(3000);
System.out.println("Asking thread to stop...");
// 发出中断请求
thread.interrupt();
Thread.sleep(3000);
System.out.println("Stopping application...");
}
public void run() {
// 每隔一秒检测是否设置了中断标示
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Thread is running...");
long time = System.currentTimeMillis();
// 使用while循环模拟 sleep
while ((System.currentTimeMillis() - time < 1000) ) {
}
}
System.out.println("Thread exiting under request...");
}
}
2、使用中断信号量中断非阻塞状态的线程
class Example2 extends Thread {
volatile boolean stop = false;// 线程中断信号量
public static void main(String args[]) throws Exception {
Example2 thread = new Example2();
System.out.println("Starting thread...");
thread.start();
Thread.sleep(3000);
System.out.println("Asking thread to stop...");
// 设置中断信号量
thread.stop = true;
Thread.sleep(3000);
System.out.println("Stopping application...");
}
public void run() {
// 每隔一秒检测一下中断信号量
while (!stop) {
System.out.println("Thread is running...");
long time = System.currentTimeMillis();
/*
* 使用while循环模拟 sleep 方法,这里不要使用sleep,否则在阻塞时会 抛
* InterruptedException异常而退出循环,这样while检测stop条件就不会执行,
* 失去了意义。
*/
while ((System.currentTimeMillis() - time < 1000)) {}
}
System.out.println("Thread exiting under request...");
}
}
线程中断参考:线程中断详解