一、图解方法
二、sleep方法详解:
1.sleep方法特点
以下代码运行了两个Thread,等第一个Thread执行完,第二个Thread才能执行,可见sleep方法并不能释放锁。
public class SleepDontReleaseMonitor implements Runnable {
@Override
public void run() {
syn();
}
public static void main(String[] args) {
SleepDontReleaseMonitor sleepDontReleaseMonitor = new SleepDontReleaseMonitor();
new Thread(sleepDontReleaseMonitor).start();
new Thread(sleepDontReleaseMonitor).start();
}
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() + "退出了同步代码块");
}
}
运行结果:
线程Thread-0获取到了monitor。
线程Thread-0退出了同步代码块
线程Thread-1获取到了monitor。
线程Thread-1退出了同步代码块
换成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();
}
}
运行结果:
线程Thread-0获取到了锁
线程Thread-0已经苏醒
线程Thread-1获取到了锁
线程Thread-1已经苏醒
2.sleep方法的优雅写法
除了优雅之外,此种方法还能避免传参错误发生的异常。
public class SleepInterrupted implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(new Date());
try {
TimeUnit.HOURS.sleep(3); //更优雅
TimeUnit.MINUTES.sleep(25);
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("我被中断了!");
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new SleepInterrupted());
thread.start();
Thread.sleep(6500);
thread.interrupt();
}
}
3. sleep方法的总结:
sleep方法可以让线程进入WAITING状态,并且不占用CPU资源,但是不释放锁。直到规定时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态。
常见问题:
wait/notify、sleep异同?
相同点:都会进入阻塞状态,而且会相应Interrupt中断
不同点:
wait/notify方法需要在同步方法中执行,sleep方法不需要
wait/notify方法释放锁,sleep方法不释放锁
wait/notify可以不指定时间,sleep需要传时间参数
wait/notify属于Object方法,而sleep属于Thread类,所属类不同
三、join方法详解
1. join方法的基本使用
作用:因为新的线程加入了我们,所以我们要等他执行完再出发
用法:main等待thread1执行完毕,注意谁等谁
用代码来看一下Join的一般用法:
public class Join {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行完毕");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行完毕");
}
});
thread.start();
thread2.start();
System.out.println("开始等待子线程运行完毕");
thread.join();
thread2.join(); //让主线程等待thread和thread2执行完毕
System.out.println("所有子线程执行完毕");
}
}
运行结果:
开始等待子线程运行完毕
Thread-1执行完毕
Thread-0执行完毕
所有子线程执行完毕
结果分析:如果没有Join方法,运行结果一定是thread中的方法最后运行完毕。可见Join方法的效果与用处。
2.join方法的中断
子线程加入了主线程,在加入的过程中,到底是谁被中断了呢,子线程还是主线程?
看一下代码演示:
public class JoinInterrupt {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
mainThread.interrupt();
Thread.sleep(5000);
System.out.println("Thread1 finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread1.start();
System.out.println("等待子线程执行完毕");
try { //主线程等待期间被打断,打断的是主线程,而不是子线程
thread1.join();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "主线程被中断了");
e.printStackTrace();
// thread1.interrupt();
}
System.out.println("子线程已经运行完毕");
}
}
运行结果:
等待子线程执行完毕
java.lang.InterruptedException
main主线程被中断了
子线程已经运行完毕
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1245)
at java.lang.Thread.join(Thread.java:1319)
at threadobjectclasscommonmethods.JoinInterrupt.main(JoinInterrupt.java:29)
Thread1 finished
可见运行的是thread1.join(),而实际上被中断的却是主线程,需要再次执行thread1.interrupt(),才能中断thread1
那么在主线程等待子线程join的时候,主线程的状态是什么呢?代码演示如下:
public class JoinThreadState {
public static void main(String[] args) throws InterruptedException {
Thread mainThread = Thread.currentThread();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
System.out.println("主线程的运行状态:" + mainThread.getState()); //WAITING
System.out.println("子线程的运行状态:" + Thread.currentThread().getState());
System.out.println("Thread-0运行结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
System.out.println("等待子线程运行完毕");
thread.join();
System.out.println("子线程运行完毕");
}
}
运行结果:
等待子线程运行完毕
主线程的运行状态:WAITING
子线程的运行状态:RUNNABLE
Thread-0运行结束
子线程运行完毕
可见,在主线程等待子线程join的时候,主线程的状态为WAITIGNG,子线程为RUNNABLE
3.被封装好的,功能相同的类:CountDownLatch或CyclicBarrier
4.Join方法源码解析:
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;
}
}
}
看见,原理主要使用了wait方法,但是wait之后谁来notify呢,原来Thread类在结束之后会主动执行notifyAll,看过原理,那么上面的代码可等价为:
public class JoinPrinciple {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行完毕");
}
});
thread.start();
System.out.println("开始等待子线程运行完毕");
// thread.join();
synchronized (thread){
thread.wait();
}
System.out.println("所有子线程执行完毕");
}
}