Object的notify和notifyAll方法
我们除了给线程设置等待时间让系统自动唤醒线程外,我们还可以通过notify或者notifyAll手动唤醒线程.
案例演示:
public class WaitSleepTest { public static void main(String[] args) { final Object lock = new Object(); //通过wait执行等待 new Thread(new Runnable() { @Override public void run() { System.out.println("threadA is waiting to get lock..."); synchronized (lock) { try { System.out.println("ThreadA get lock."); Thread.sleep(20); System.out.println("threadA do wait method"); //让线程A无限期等待 lock.wait(); System.out.println("threadA is done"); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); //由于哪个线程先执行是不一定的,所以我们在这里让主线程休眠一会,等一会再让主线程去启动第二个线程 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //通过sleep执行等待 new Thread(new Runnable() { @Override public void run() { System.out.println("threadB is waiting to get lock..."); synchronized (lock) { try { System.out.println("ThreadB get lock."); System.out.println("threadB is sleeping 10 ms"); Thread.sleep(10); System.out.println("threadB is done"); //线程B执行结束后手动唤醒线程A lock.notify(); //这个效果也一样lock.notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } } ---运行结果 threadA is waiting to get lock... ThreadA get lock. threadB is waiting to get lock... threadA do wait method ThreadB get lock. threadB is sleeping 10 ms threadB is done threadA is done 如果没有手动唤醒线程A,那么服务器将一直处于启动状态,最后一句话也不会打印出来.两个概念
对于jvm中运行程序的每个对象都有这两个池
锁池EntryList(也可以理解为同步队列)等待池WaitList(也可以理解为等待队列)锁池EntryList 我们上面的案例的代码中就是线程A拥有了lock对象的锁,在线程A没有进去wait()方法释放锁之前,线程B就只能去线程池中等着.
等待池WaitList 就是我们上面的例子中线程A调用了wait方法后他就去了等待池,然后线程B就可以占有lock的锁并执行synchronized方法了,并且,只有当线程B执行完方法或者抛出异常之后才会释放锁.
notify和notifyAll方法的区别
notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会,而且在此之后没有获取到锁就会被放到在锁池中等待其他机会去获取锁,不会主动回到等待池了(不过可以通过wait()方法回去哈哈哈).notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会.Thread类中的yield
通过看源码知道,这是一个native方法.
概念: 当调用Thread.yield()方法时,会给线程调度器一个 当前线程愿意让出CPU使用权的暗示,但是线程调度器可能会忽略这个暗示.
注意: yield()方法不会对锁的行为造成影响.
案例:
public class YieldTest { public static void main(String[] args) { Runnable yieldTask = new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + i); //当i为5时,该线程表示愿意让出CPU使用权,线程调度器可能用也可能不用这个暗示 //如果用了,那么i=5时就会暂停该线程去执行另一个线程,如果不用,两个就抢吧就 if (i == 5) { Thread.yield(); } } } }; Thread t1 = new Thread(yieldTask,"A"); Thread t2 = new Thread(yieldTask,"B"); t1.start(); t2.start(); } } ---运行结果:这是不一定的,因为调度器不一定用,所以这只是参考的一部分结果 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9如何中断线程
目前使用的方法: 调用interrupt()方法,通知线程应该中断了,但是具体要不要中断线程需要由被通知的线程自己决定.
如果线程处于阻塞状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常.如果线程处于正常活动状态, 那么会将该线程的中断标志设置为true,被设置中断标志的线程将继续正常进行,不受影响.由上面我们可以知道,interrupt并不会去中断线程,它和yield一样,只是作为一个hint使用.
需要被调用的线程自己配合中断
即一个线程如果有被中断的需求,那就可以通过interrupt这种方法.
在正常运行任务中,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程.也可以在阻塞的线程抛出异常后,捕获异常后就停止该线程(退出run方法就可以了).如果线程处于正常活动状态, 那么会将该线程的中断标志设置为true,被设置中断标志的线程将继续正常进行,不受影响.