1 线程状态 1.1.2
- New:尚未启动的线程,创建了没启动;
- Runnable:可运行线程的状态,等待CPU调度;
- Blocked:线程阻塞等待监视器锁定的状态;处于synchronized同步代码块或方法中被阻塞。
- Waiting:等待线程的线程状态。下列不带超时的方式:Object.wait/Thread.join/LockSupport.park
- Timed Waiting:具有指定等待时间的等待线程的线程状态。下列带超时的方式:Thread.sleep/Object.wait/Thread.join/LockSupport.parkNanos/LockSupport.parkUntil
- Terminated:终止线程的线程状态。
2 线程状态的跳转
3 线程的终止 1.1.3
3.1 不合理的终止
首先需要终止的线程是这样的:
public class StopThread extends Thread {
private int i = 0, j = 0;
@Override
public void run() {
synchronized (this) {
// 增加同步锁,确保线程安全
++i;
try {
// 休眠10秒,模拟耗时操作
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
++j;
}
}
/** * 打印i和j */
public void print() {
System.out.println("i=" + i + " j=" + j);
}
}
不合理的终止,就是用线程的stop方法进行终止,其实这个方法已经被废弃了。
public static void main(String[] args) throws InterruptedException {
StopThread thread = new StopThread();
thread.start();
// 休眠1秒,确保i变量自增成功
Thread.sleep(1000);
// 暂停线程
// thread.stop(); // 错误的终止
thread.interrupt(); // 正确终止
while (thread.isAlive()) {
// 确保线程已经终止
} // 输出结果
thread.print();
}
用stop方法的结果打印是i=1,j=0;用interrupt方法的结果打印是i=1,j=1;
小结:如果线程调用了wait,sleep,join等,被阻塞了,那么interrupt方法会生效,线程的中断状态会被清除。
4 线程之间的通信 1.1.5
要实现多个线程之间的协同,如:线程的先后执行控制,获取某个线程执行的结果等等。涉及线程之间的相互通信,分为下面四类: 1、文件共享;2、网络共享;3、变量共享;4、JDK提供的线程协调API。前面这3点很好理解,就是多个线程,大家一起去操作一个公共的东西。第四点主要有wait/notify和park/unpark,suspend/resume已经被弃用了。
4.1 Wait/Notify
这些方法只能由同一个对象锁的持有者线程调用,也就是写在同步代码块中,否则会抛出异常。wait方法会导致当前线程等待,加入到该对象的等待集合中,并且放弃当前持有的对象锁。notify/notifyall方法唤醒一个或者多个正在等待这个对象锁的线程。注意wait虽然会自动释放锁,但是有顺序要求,如果在notify之后才开始wait,那么就会出问题。
4.2 park/unpark
park和unpark是不会释放锁的,如果在同步代码块中加了park/unpark,先后顺序出了问题,就会导致死锁。
4.3 伪唤醒
线程可能会被某种异常伪唤醒,如果用if判断某个条件是否发生,那么线程就走下去了,所以最好改成用while循环判断,这样就不会被伪唤醒影响。
new Thread(() -> {
synchronized (this) {
while (baozidian == null) { // 如果没包子,则进入等待
try {
System.out.println("1、进入等待");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("2、买到包子,回家");
}).start();
5 线程封闭 1.1.6
多线程访问共享可变数据时,涉及到线程间数据的同步问题,并不是所有时候都需要用到共享数据,所以就提出了线程封闭的概念。数据都被封闭在各自的线程之中,就不需要同步了。线程封闭的具体体现有ThreadLocal和局部变量。这里主要说说ThreadLocal。
public class Demo6_my {
public static ThreadLocal<String> value=new ThreadLocal<>();
public static void main(String[] args) {
value.set("123 from main");
new Thread(()->{
System.out.println("value= "+value.get());
value.set("456 from"+Thread.currentThread().getName());
System.out.println("value= "+value.get());
}).start();
System.out.println("In main: "+value.get());
}
}