1.1 线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
例如 有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。
特殊例子 sleep(0) 作用就是让低优先级的线程有机会执行。
总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
1.2 在JDK 1.5之后,使用 TimeUnit.MILLISECONDS.sleep(100); 代替sleep(100).
需要注意,使用sleep(100)并不能保证100毫秒后此线程立刻执行,具体在CPU分配
2. wait(),notify(),notifyAll()
2.1 线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。
这三个方法用于协调多个线程对共享数据的存取,所以必须在同步语句块内使用。synchronized关键字用于保护共享数据,阻止其他线程对共享数据的存取,但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出synchronized数据块时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制。
wait()方法使当前线程暂停执行并释放对象锁,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中。当调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
一个经典的例子说明wait(),notify()的使用:
开启至少三个线程,循环打印ABC十次:下面是我自己写的实现,可能有瑕疵。
public class WaitThread implements Runnable {
private static volatile boolean one = true;
private static volatile boolean two = false;
private static volatile boolean three = false;
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (this) {
if (one) {
notify(); //唤醒阻塞的线程
System.out.println("A");
one = false;
two = true;
three = false;
} else {
try {
this.wait();//线程阻塞
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
synchronized (this) {
if (two) {
notify();
System.out.println("B");
one = false;
two = false;
three = true;
} else {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
synchronized (this) {
if (three) {
notify();
System.out.println("C");
one = true;
two = false;
three = false;
} else {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
WaitThread waitThread = new WaitThread();
new Thread(waitThread).start();
new Thread(waitThread).start();
new Thread(waitThread).start();
}
}
3. sleep() 和wait() 的区别。
3.1 sleep()用于线程控制,而wait()用于线程间的通信,由于wait函数的特殊意义,所以他是应该放在同步语句块中的,这样才有意义.
3.2 sleep(10) 在设定的毫秒数之后可能被唤醒《注意不是一定》,而wait()使当前线程阻塞,但是此线程么有结束,一直被监听,直到 出现 notify() , notifyAll()时候,此线程继续执行。也可以指定时间,自动唤醒。
4. yield()
4.1 线程让步:Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。注意:这里的其他也包含当前线程。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
实际上,yield()方法对应了如下操作;先检测当前是否有相同优先级的线程处于可运行状态,如有,则把CPU的占有权交给此线程,否则继续运行原来的线程,所以yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。
5. sleep()和yield()区别
sleep 方法允许较低优先级的线程获得运行机会,但yield()方法执行时,当前线程仍处在可运行状态,所以不可能让出较低优先级的线程此时获取CPU占有权。在一个运行系统中,如果较高优先级的线程没有调用sleep方法,也没有受到I/O阻塞,那么较低优先级线程只能等待所有较高优先级的线程运行结束,方可有机会运行。
6. join()
join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个线程运行结束,当前线程再由阻塞转为就绪状态。
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
public class JoinTest implements Runnable{
public static int a = 0;
public void run() {
for (int k = 0; k < 5; k++) {
a = a + 1;
}
}
public static void main(String[] args) throws Exception {
Runnable r = new JoinTest();
Thread t = new Thread(r);
t.start();
//t.join();
System.out.println(a);
}
}
如上,如果我们注释掉t.join();线程大部分时候可能不会输出5,因为当主线程 main方法执行System.out.println(a);这条语句时,线程还没有真正开始运行,或许正在为它分配资源准备运行。因为为线程分配资源需要时间,而main方法执行完t.start()方法后继续往下执行System.out.println(a);,这个时候得到的结果是a还没有被 改变的值0 。怎样才能让输出结果为5!其实很简单,join() 方法提供了这种功能。join() 方法,它能够使调用该方法的线程在此之前执行完毕。