1 方法概览
类 | 方法名 | 简介 |
Thread | sleep相关 | |
join | 等待其他线程执行完毕 | |
yield相关 | 放弃已经获取到的CPU资源 | |
currentThread | 获取当前执行线程的引用 | |
start/run相关 | 启动线程相关 | |
interrupt相关 | 中断线程 | |
已废弃 | ||
Object | wait/notify/notifyAll相关 | 让线程暂时休息和唤醒(需要被synchronized保住) |
/**
* 演示打印main, Thread-0, Thread-1
*/
public class CurrentThread implements Runnable {
public static void main(String[] args) {
CurrentThread tread = new CurrentThread();
tread.run();
new Thread(tread).start();
new Thread(tread).start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
2 wait/notify/notifyAll
2.1 注意点
wait/notify/notifyAll 需要放在synchronized快里面
https://blog.csdn.net/sarafina527/article/details/89173738
public class Main {
private final static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
try {
lock.wait();
} catch (InterruptedException ignored) {
}
}).start();
}
}
//Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
// at java.base/java.lang.Object.wait(Native Method)
// at java.base/java.lang.Object.wait(Object.java:326)
// at UseOwnUncaughtExceptionHandler.lambda$main$0(UseOwnUncaughtExceptionHandler.java:7)
// at java.base/java.lang.Thread.run(Thread.java:830)
public class Main {
private final static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " exec wait start.");
try {
lock.wait();
} catch (InterruptedException ignored) {
}
System.out.println(Thread.currentThread().getName() + " exec wait end.");
}
}, "AAA").start();
TimeUnit.SECONDS.sleep(1);
synchronized (lock) { // 这里锁的对象要和wait哪个一致,不然会报异常或者是效果不是我想要的
lock.notify();
}
TimeUnit.SECONDS.sleep(1);
System.out.println("main exec success.");
}
}
//AAA exec wait start.
//AAA exec wait end.
//main exec success.
调用notify()后,并不会立即释放锁,而是继续执行当前代码,直到synchronized中的代码全部执行完毕,才会释放对象锁
https://www.cnblogs.com/benshan/p/3551987.html
public class Main {
private final static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " login synchronized.");
try {
System.out.println(Thread.currentThread().getName() + " exec wait.");
lock.wait();
System.out.println(Thread.currentThread().getName() + " awaken.");
} catch (InterruptedException ignored) {
}
}
System.out.println(Thread.currentThread().getName() + " logout synchronized.\n");
}, "AAA").start();
TimeUnit.SECONDS.sleep(1); // 等AAA先执行
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " exec notify.");
lock.notify(); // 虽然这里就执行了notify,但是程序要走出synchronized,AAA线程才会被唤醒
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName() + " logout synchronized.");
}
TimeUnit.SECONDS.sleep(5);
System.out.println("main exec success.");
}
}
2.2 基本用法
作用/用法:阻塞阶段、唤醒阶段、遇到中断
/**
* 展示wait和notify的基本用法
* 1. 研究代码执行顺序
* 2. 证明wait释放锁
*/
public class Wait {
public static Object object = new Object();
static class Thread1 extends Thread {
@Override
public void run() {
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "开始执行了");
try {
//释放monitor锁
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁。");
}
}
}
static class Thread2 extends Thread {
@Override
public void run() {
synchronized (object) {
object.notify();
System.out.println("线程" + Thread.currentThread().getName() + "调用了notify()");
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2();
thread1.start();
Thread.sleep(200);
thread2.start();
}
}
/**
* 3个线程,线程1和线程2首先被阻塞,线程3唤醒它们。notify, notifyAll。 start先执行不代表线程先启动。
*/
public class WaitNotifyAll implements Runnable {
private static final Object resourceA = new Object();
@Override
public void run() {
synchronized (resourceA) {
System.out.println(Thread.currentThread().getName()+" got resourceA lock.");
try {
System.out.println(Thread.currentThread().getName()+" waits to start.");
resourceA.wait();
System.out.println(Thread.currentThread().getName()+"'s waiting to end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args){
Runnable r = new WaitNotifyAll();
Thread threadA = new Thread(r);
Thread threadB = new Thread(r);
Thread threadC = new Thread(() -> {
synchronized (resourceA) {
resourceA.notifyAll();
//resourceA.notify();
System.out.println("ThreadC notified.");
}
});
threadA.start();
threadB.start();
//Thread.sleep(200);
threadC.start();
}
}
wait只释放当前的那把锁
/**
* 证明wait只释放当前的那把锁(只释放当前monitor)
*/
public class WaitNotifyReleaseOwnMonitor {
private static Object resourceA = new Object();
private static Object resourceB = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (resourceA) {
System.out.println("ThreadA got resourceA lock.");
synchronized (resourceB) {
System.out.println("ThreadA got resourceB lock.");
try {
System.out.println("ThreadA releases resourceA lock.");
//释放A
resourceA.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread thread2 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resourceA) {
System.out.println("ThreadB got resourceA lock.");
System.out.println("ThreadB tries to resourceB lock.");
synchronized (resourceB) {
System.out.println("ThreadB got resourceB lock.");
}
}
});
thread1.start();
thread2.start();
}
}
2.3 wait(timeoutMillis)
https://www.cnblogs.com/zerodsLearnJava/p/12852891.html
public class Main {
private final static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " exec wait start.");
try {
/**
* 没有其他线程执行lock.notify(),并且能再次获得monitor锁就等5s,不能获取就一直等
* 有其他线程执行lock.notify(),不用等5s
*/
lock.wait(5000L); //
} catch (InterruptedException ignored) {
}
System.out.println(Thread.currentThread().getName() + " exec wait end.");
}
}, "AAA").start();
TimeUnit.SECONDS.sleep(1);
synchronized (lock) {
//lock.notify(); // lock.wait(5000L); 不用等5s就能向下执行
TimeUnit.SECONDS.sleep(100); // 不释放锁,lock.wait(5000L); 时间到了也不会继续往下执行的
}
TimeUnit.SECONDS.sleep(1);
System.out.println("main exec success.");
}
}
2.4 手写生产者消费者设计模式
/**
* 用wait/notify来实现生产者消费者模式
*/
public class ProducerConsumerModel {
public static void main(String[] args) {
EventStorage eventStorage = new EventStorage();
Producer producer = new Producer(eventStorage);
Consumer consumer = new Consumer(eventStorage);
new Thread(producer).start();
new Thread(consumer).start();
}
}
class Producer implements Runnable {
private EventStorage storage;
public Producer(EventStorage storage) {
this.storage = storage;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
storage.put();
}
}
}
class Consumer implements Runnable {
private EventStorage storage;
public Consumer(EventStorage storage) {
this.storage = storage;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
storage.take();
}
}
}
//实现效果类似于阻塞队列
class EventStorage {
private int maxSize;
private LinkedList<Date> storage;
public EventStorage() {
maxSize = 10;
storage = new LinkedList<>();
}
// 生产者 添加
public synchronized void put() {
while (storage.size() == maxSize) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
storage.add(new Date());
System.out.println("仓库里有了" + storage.size() + "个产品。");
this.notify();
}
// 消费者 消费
public synchronized void take() {
while (storage.size() == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("拿到了" + storage.poll() + ",现在仓库还剩下" + storage.size());
this.notify();
}
}
2.5 两个线程交替打印0~100的奇偶数
- 偶线程:0
- 奇线程:1
- 偶线程:2
/**
* 两个线程交替打印0~100的奇偶数,用synchronized关键字实现
*/
public class WaitNotifyPrintOddEvenSyn {
private static int count;
private static final Object lock = new Object();
//新建2个线程
//1个只处理偶数,第二个只处理奇数(用位运算)
//用synchronized来通信
public static void main(String[] args) {
new Thread(() -> {
while (count < 100) {
synchronized (lock) {
if ((count & 1) == 0) {
System.out.println(Thread.currentThread().getName() + ":" + count++);
}
}
}
}, "偶数").start();
new Thread(() -> {
while (count < 100) {
synchronized (lock) {
if ((count & 1) == 1) {
System.out.println(Thread.currentThread().getName() + ":" + count++);
}
}
}
}, "奇数").start();
}
}
第一种方法虽然能实现效果,但是有很多不必要的运算
/**
* 两个线程交替打印0~100的奇偶数,用wait和notify
*/
public class WaitNotifyPrintOddEveWait {
private static int count = 0;
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(new TurningRunner(), "偶数").start();
//Thread.sleep(100);
new Thread(new TurningRunner(), "奇数").start();
}
//1. 拿到锁,我们就打印
//2. 打印完,唤醒其他线程,自己就休眠
static class TurningRunner implements Runnable {
@Override
public void run() {
while (count <= 100) {
synchronized (lock) {
//拿到锁就打印
System.out.println(Thread.currentThread().getName() + ":" + count++);
lock.notify();
if (count <= 100) {
try {
//如果任务还没结束,就让出当前的锁,并休眠
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
3 线程休眠sleep方法
3.1 sleep用法
- 作用:我只想让线程在预期的时间执行,其他时候不要占用CPU资源
- 不释放锁:包括synchronized和lock (和wait不同)
/**
* 展示线程sleep的时候不释放synchronized的monitor,等sleep时间到了以后,正常结束后才释放锁
*/
public class SleepDontReleaseMonitor implements Runnable {
public static void main(String[] args) {
SleepDontReleaseMonitor sleepDontReleaseMonitor = new SleepDontReleaseMonitor();
new Thread(sleepDontReleaseMonitor).start();
new Thread(sleepDontReleaseMonitor).start();
}
@Override
public void run() {
syn();
}
private synchronized void syn() {
System.out.println("线程" + Thread.currentThread().getName() + "获取到了monitor。");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() + "退出了同步代码块");
}
}
/**
* 演示sleep不释放lock(lock需要手动释放)
*/
public class SleepDontReleaseLock implements Runnable {
private static final Lock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();
System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁");
try {
Thread.sleep(5000);
System.out.println("线程" + Thread.currentThread().getName() + "已经苏醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
SleepDontReleaseLock sleepDontReleaseLock = new SleepDontReleaseLock();
new Thread(sleepDontReleaseLock).start();
new Thread(sleepDontReleaseLock).start();
}
}
3.2 sleep方法响应中断
- 抛出InterruptedException
- 清除中断状态
/**
* 每个1秒钟输出当前时间,被中断,观察。
* 1.Thread.sleep()
* 2.TimeUnit.SECONDS.sleep()
*/
public class SleepInterrupted implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new SleepInterrupted());
thread.start();
Thread.sleep(6500);
thread.interrupt();
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(new Date());
try {
//sleep:3个小时25分钟1秒
TimeUnit.HOURS.sleep(3);
TimeUnit.MINUTES.sleep(25);
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("我被中断了!");
e.printStackTrace();
}
}
}
}
一句话总结
- sleep方法可以让线程进入Waiting状态,并且不占用CPU资源,但是不释放锁,直到规定时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态
public class Main {
public static void main(String[] args) {
Thread myThread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//false 清除中断状态
System.out.println(Thread.currentThread().isInterrupted());
}
});
myThread.start();
//false
System.out.println(myThread.isInterrupted());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThread.interrupt();
}
}
3.3 sleep方法—常见面试问题
wait/notify、sleep异同(方法属于哪个对象?线程状态怎么切换?)
相同
- 阻塞
- 响应中断
不同
- 同步方法中
- 释放锁
- 指定时间
- 所属类
4 等待其他线程join方法
- 作用:因为新的线程加入了我们,所以我们要等他执行完再出发
- 用法:main等待thread1执行完毕
/**
* 演示join,注意语句输出顺序,会变化。
*/
public class Join {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行完毕");
});
Thread thread2 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行完毕");
});
thread.start();
thread2.start();
System.out.println("开始等待子线程运行完毕");
//主线程(main)等待着两个子线程执行完,在往下执行
thread.join();
thread2.join();
System.out.println("所有子线程执行完毕");
}
}
/**
* 演示join期间被中断的效果
*/
public class JoinInterrupt {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
Thread thread1 = new Thread(() -> {
try {
mainThread.interrupt();
Thread.sleep(5000);
System.out.println("Thread1 finished.");
} catch (InterruptedException e) {
System.out.println("子线程中断");
}
});
thread1.start();
System.out.println("等待子线程运行完毕");
try {
thread1.join();
} catch (InterruptedException e) {//!!!这里的异常是主线程抛出的
System.out.println(Thread.currentThread().getName()+"主线程中断了");
thread1.interrupt();
}
System.out.println("子线程已运行完毕");
}
}
/**
* 1.先join再mainThread.getState()
* 2.通过debugger看线程join前后状态的对比
*/
public class JoinThreadState {
public static void main(String[] args) throws InterruptedException {
Thread mainThread = Thread.currentThread();
Thread thread = new Thread(() -> {
try {
Thread.sleep(3000);
System.out.println(mainThread.getState());
System.out.println("Thread-0运行结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
System.out.println("等待子线程运行完毕");
thread.join();
System.out.println("子线程运行完毕");
}
}
!!! 每一个Thread类在run方运行结束后会自动执行notify类似的操作 (Thread.wait() 不建议这样使用 )
//java.lang.Thread#join(long)
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
等效代码
/**
* 通过join原理,分析出join的代替写法
*/
public class JoinPrinciple {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行完毕");
//自动执行notify类似的操作
});
thread.start();
System.out.println("开始等待子线程运行完毕");
//thread.join();
synchronized (thread) {
thread.wait();
}
System.out.println("所有子线程执行完毕");
}
}
CountDownLatch或CyclicBarrier类
5 线程让步yield方法
- 作用:释放我的CPU时间片
- 定位:JVM不保证遵循
yield和sleep区别:是否随时可能再次被调度
6 常见面试问题
1. 为什么线程通信的方法wait(),notify()和notifyAll()被定义在Object类里?而sleep定义在Thread类里?
2. 用3种方式实现生产者模式
3. Java SE 8和Java 1.8和JDK8是什么关系,是同一个东西吗?
4. Join和sleep和wait期间线程的状态分别是什么?为什么?