signalAll()和synchronized
当我试着使用lock来控制锁的,发现以下有趣的现象
public class WaxOMatic {
public static void main(String[] args) throws InterruptedException {
Car car = new Car();
ExecutorService s = Executors.newFixedThreadPool(2);
s.execute(new Thread(new WaxOn(car)));
s.execute(new Thread(new WaxOff(car)));
TimeUnit.SECONDS.sleep(5);
// s.shutdownNow();
}
}
class Car {
private boolean waxOx = false;
Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public synchronized void waxed() {
/* lock.lock();
try {*/
waxOx = true;
notifyAll();
// condition.signalAll();
/* } finally {
lock.unlock();
}*/
}
public void buffed() {
lock.lock();
try {
waxOx = false;
// notifyAll();
condition.signalAll();
} finally {
lock.unlock();
}
}
public /** synchronized */
void waitForWaxing() throws InterruptedException {
lock.lock();
try {
System.out.println();
while (waxOx == false) {
System.out.println("waitfor ..... ");
// wait();
condition.await();
}
} finally {
lock.unlock();
}
}
public /** synchronized */
void waitForBuffing() throws InterruptedException {
lock.lock();
try {
while (waxOx == true) {
// wait();
condition.await();
}
} finally {
lock.unlock();
}
}
}
class WaxOn implements Runnable {
private Car car;
public WaxOn(Car car) {
this.car = car;
}
@Override
public void run() {
while (!Thread.interrupted()) {
System.out.println("start wax on .....");
// System.out.println("name = " + Thread.currentThread().getName());
try {
TimeUnit.MILLISECONDS.sleep(200);
car.waxed();
System.out.println(" --- wax ----");
car.waitForBuffing();
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
System.out.println("end waxing ......");
}
}
}
class WaxOff implements Runnable {
private Car car;
public WaxOff(Car car) {
this.car = car;
}
@Override
public void run() {
System.out.println("start buffed ...");
try {
while (!Thread.interrupted()) {
car.waitForWaxing();
System.out.println(" ---- buf ---");
TimeUnit.MILLISECONDS.sleep(200);
car.buffed();
}
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
} finally {
}
System.out.println("end buffed /...");
}
}
可以看到waxed()方法,使用了synchronize获取锁,其他则使用了lock,
在执行过程中,发现,WaxOn给Car加工后按理说应该唤醒WaxOff了,因为在加工完成后,调用了notifyAll(),并且改变控制信号错失的值为true,随后自己进行await(),但是却没有唤醒WaxOff()方法.
第一行表示WaxOf已经给Car开始加工了
第二行语句代表WaxOff已经启动,因为因为需要等待WaxOn加工后所以才能操作,所以进入await(),等待被WaxOn加工完成,唤醒执行
第三行,这样WaxOn中加工已经完成了,一直在等待WaxOff执行
但是WaxOff却没有被唤醒执行后续的加工,整个线程被锁了我们用jstack看看状态
可以看到,WaxOff还在等待WaxOn的加工完成的唤醒,由于WaxOn加工后进行了notifyAll,但是却并没有唤醒,使WaxOff一直陷入waiting,然后WaxOn加工后自己也进入了waiting,等待被WaxOff处理。所以这里我们看到了两个线程都进入了waiting,都需要被对方等待唤醒,但却永远不会有操作,陷入了死锁.
原因
因为waxed使用synchronized获取锁,而其他则使用了lock,由于synchronized和lock获取锁的方式不同,condition 来自于某个lock.,通过lock.lock()获取锁,object 通过.synconize() 获取锁,这样就导致他们获取的不是同一个锁,释放了锁,但是却不是对方所需要的锁,故而对着需要该锁的当前线程的notifyAll并不是对方的notifyAll。
中断注意事项
public class WaxOMatic {
public static void main(String[] args) throws InterruptedException {
Car car = new Car();
ExecutorService s = Executors.newFixedThreadPool(2);
s.execute(new Thread(new WaxOn(car)));
s.execute(new Thread(new WaxOff(car)));
TimeUnit.SECONDS.sleep(5);
s.shutdownNow();
}
}
class Car{
private boolean waxOx = false;
public synchronized void waxed(){
waxOx = true;
notifyAll();
}
public synchronized void buffed(){
waxOx = false;
notifyAll();
}
public synchronized void waitForWaxing() throws InterruptedException {
while (waxOx == false) {
wait();
}
}
public synchronized void waitForBuffing() throws InterruptedException {
while (waxOx == true){
wait();
}
}
}
class WaxOn implements Runnable{
private Car car;
public WaxOn(Car car) {
this.car = car;
}
@Override
public void run() {
while (!Thread.interrupted()){
System.out.println("start wax on .....");
System.out.println("name = " + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
car.waxed();
System.out.println(" --- wax ----");
car.waitForBuffing();
} catch (InterruptedException e) {
e.printStackTrace();
// Thread.currentThread().interrupt();
}
System.out.println("end waxing ......");
}
}
}
class WaxOff implements Runnable{
private Car car;
public WaxOff(Car car) {
this.car = car;
}
@Override
public void run() {
System.out.println("start buffed ...");
try {
while (!Thread.interrupted()){
car.waitForWaxing();
System.out.println(" ---- buf ---");
TimeUnit.MILLISECONDS.sleep(200);
car.buffed();
}
} catch (InterruptedException e) {
e.printStackTrace();
// Thread.currentThread().interrupt();
} finally {
}
System.out.println("end buffed /...");
}
}
以上代码是thinking in java中的demo,发现一个奇怪的问题,线程在中断后有一个奇怪现象,在执行中断操作后,线程又重新跑了一次。
经过思考原因,反复论证,中断操作对于 I/O,尝试获取锁无法中断,对于sleep,wait等的阻塞状态中断会发生InterruptedException异常,在这里我们需要处理中断,Thread.currentThread().interrupt();//重新设置中断标示位,让线程从阻塞中退出。
如图,加上中断处理
设置中断位后,都已经结束了.线程也没有挂起,中断起到作用了。
总结
对于处于wait(),sleep,join等操作的线程,如果被调用interrupt()后,会立即重置中断位为false和抛出InterruptedException,因为线程为了处理异常已经重新处于就绪状态。,再一次重新启动的原因就是这个,线程在触发中断的异常后,便会再让这个线程重新处于就绪,重新执行。可以再捕获InterruptedException后return,break或者再一次Thread.interrupted()再次设置中断位也可以起到中断线程的作用