基本概念
中断
- boolean interrupted() 返回当前调用线程,不是调用该方法的实例对象是否中断,若是中断,清除中断标志位
- boolean isinterrupted() 返回调用线程是否中断**,若是中断,恢复中断标志位**
- void interupt() 将调用线程中断标志为true
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
可以看到interrupted返回的是当前调用线程的中断状态
public class Test {
public static void main(String[] args) throws InterruptedException{
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
for(;;){}
}
});
thread.start();
System.out.println(thread.isInterrupted());
thread.interrupt();
System.out.println(thread.isInterrupted());
System.out.println(thread.interrupted());
System.out.println(Thread.interrupted());
System.out.println(thread.isInterrupted());
}
}
false
true // 还是中断状态
false // 此处是主线程的中断状态
false
true
wait/park/sleep/await/yield/join
- wait/(notify | notify) wait或者notify的前提是获得共享变量的锁(监视器),wait了之后会放弃持有锁,同样得到锁才能notify进行唤醒,唤醒后也不是立刻就能执行,还需要重新获得锁。
public class Test {
public static void main(String[] args) throws InterruptedException {
Integer a = 3; // 共享变量
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (a) {
try {
System.out.println("before wait "+System.currentTimeMillis());
a.wait(); // 调用了wait(0)
System.out.println("wait is ending "+System.currentTimeMillis());
} catch (Exception e) {
}
}
}
});
thread.start();
Thread.sleep(2000);
synchronized (a) {
a.notify();
Thread.sleep(4000);
}
}
}
before wait 1561814784666
wait is ending 1561814790685 // 隔了6000ms
可以看到,首先子线程需要获得共享变量的Monitor,才能进行wait。再接着主线程需要获得Monitor才能进行notify。同时,notify后子线程并不能立刻运行,还是需要等获得Monitor才能从wait方法返回
public final native void wait(long timeout) throws InterruptedException;
wait源码如上图所示,timeout若是0,则只能等待notify唤醒。若不是0的正数时间,在时间范围内,若是没有notify,到时间就会不再等待,返回就绪状态等Monitor。
- park/unpark LockSupport(挂起和唤醒线程的工具类)的一对。调用park方法的线程若是没有许可证,就被阻塞,直到有unpack唤醒它。或者先执行unpack给当前线程颁发许可证,之后再执行pack的话会立刻返回。
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("before park "+System.currentTimeMillis());
LockSupport.park(); // 没有许可证第一次park会被阻塞
System.out.println("after park "+System.currentTimeMillis());
}
});
thread.start();
Thread.sleep(2000);
LockSupport.unpark(thread);
}
}
before park 1561816399926
after park 1561816401934 // 第一次park被阻塞2秒
接着是先unpark发许可证,再接park,会立刻返回
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
// LockSupport.unpark(thread);
LockSupport.unpark(Thread.currentThread()); // 注意这里边应该是当前线程,运行时确定
System.out.println("before park "+System.currentTimeMillis());
LockSupport.park(); // 没有许可证第一次park会被阻塞
System.out.println("after park "+System.currentTimeMillis());
}
});
thread.start();
}
}
before park 1561817436822
after park 1561817436822 // 立刻返回
- sleep时候被中断会抛出InterruptedException,sleep不会放弃锁,处于阻塞状态,时间到了后处于就绪状态
- await condition.await类似于wait,需要先获得条件变量的锁。signal则是类似于notify;
- 线程调用yield,代表让出cpu的使用权,此时该线程处于就绪状态,cpu开始线程调度(可能还调用这个线程)。
- 调用join后当前线程一直阻塞,等待目标线程执行完毕
CountDownLatch/CyclicBarrier
- CountDownLatch
public class Test implements Runnable{
static final CountDownLatch end=new CountDownLatch(10);
static final Test TEST=new Test();
@Override
public void run() {
try {
System.out.println("test");
end.countDown(); // 计数器减一
}
catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService exec= Executors.newFixedThreadPool(10);
for(int i=0;i<10;i++){
exec.submit(TEST);
}
end.await(); // 主线程等待所有线程执行完成
System.out.println("okok");
exec.shutdown();
}
}
test
test
test
test
test
test
test
test
test
test
okok