首先从公司一道笔试题开始
1 package test; 2 3 public class Test implements Runnable { 4 5 public int i = 0; 6 7 @Override 8 public void run() { 9 try { 10 Thread.sleep(1000); 11 } catch (InterruptedException e) { 12 // TODO Auto-generated catch block 13 e.printStackTrace(); 14 } 15 i = 10; 16 } 17 18 public static void main(String[] args) { 19 try { 20 Test t = new Test(); 21 Thread th = new Thread(t); 22 th.start(); 23 th.join(); 24 System.out.println(t.i); 25 } catch (Exception ex) { 26 27 } 28 29 } 30 }
问23行代码怎么写,才能让24行打印出10?
不少笔试者会选t.wait()或者th.wait()!
面试的时候问他为什么,他具体也说不清楚,感觉就是见过这个wait方法,但是wait方法的含义确一知半解。
wait 是什么意思呢?我举例子啊,比如我想让本线程放弃当前对象锁,说直白点就是让别的对象进入同步块
1 package test; 2 3 public class Test implements Runnable { 4 5 public Object i = new Object(); 6 7 @Override 8 public void run() { 9 synchronized (i) { 10 System.out.println(Thread.currentThread().getName()+"enter "); 11 // i.notify(); 12 try { 13 i.wait(); 14 } catch (InterruptedException e) { 15 // TODO Auto-generated catch block 16 e.printStackTrace(); 17 } 18 System.out.println(Thread.currentThread().getName()+"out "); 19 } 20 } 21 22 public static void main(String[] args) { 23 try { 24 Test t = new Test(); 25 Thread th1 = new Thread(t); 26 Thread th2 = new Thread(t); 27 th1.start(); 28 th2.start(); 29 30 31 } catch (Exception ex) { 32 33 } 34 35 } 36 }
如上例,你会看到输出
Thread-0enter
Thread-1enter
不会看到
Thread-1out
Thread-0out
因为Thread-0 先获得了Object i 锁,然后运行到13行,释放了该锁,
这个时候Thread-1就获得了Object i 锁,进入了同步代码块,然后同样运行13行,也释放了该锁。
这个时候在有两个线程Thread-0和Thread-1等待获得Object i 锁,由于代码中没有调用i.notifyAll(),所以这个程序永远不会退出。
但是如果打开注释11行,那么你将会看到结果
Thread-0enter
Thread-1enter
Thread-0out
因为Thread-0 先获得了Object i 锁,然后运行到13行,释放了该锁,
这个时候Thread-1就获得了Object i 锁,进入了同步代码块,运行到11行,i.notify(),
那么这个意思就是说别的等待i锁的线程可以唤醒了,一旦我(Thread-1)释放锁(13行调用wait()),那么Thread-0就可以获得i锁继续执行了。
此程序中没有在Thread-1 释放i锁(wait())之后notify,所以永远不会看到Thread-1out
再回到这个题目,我们的意思是让主线程等待所有子线程执行完毕 ,再执行。更何况笔试题中没有锁对象。更别提wait()了。
所以,此处应该用th.join(); Thread.join()方法会阻塞主线程继续向下执行。
1 public class TestThread extends Thread 2 { 3 private CountDownLatch countDownLatch; 4 5 public TestThread(CountDownLatch countDownLatch) 6 { 7 this.countDownLatch = countDownLatch; 8 } 9 10 public void run() 11 { 12 System.out.println(this.getName() + "子线程开始"); 13 try 14 { 15 // 子线程休眠五秒 16 Thread.sleep(5000); 17 } 18 catch (InterruptedException e) 19 { 20 e.printStackTrace(); 21 } 22 23 System.out.println(this.getName() + "子线程结束"); 24 25 // 倒数器减1 26 countDownLatch.countDown(); 27 } 28 }
1 public class Main 2 { 3 public static void main(String[] args) 4 { 5 long start = System.currentTimeMillis(); 6 7 // 创建一个初始值为5的倒数计数器 8 CountDownLatch countDownLatch = new CountDownLatch(5); 9 for(int i = 0; i < 5; i++) 10 { 11 Thread thread = new TestThread(countDownLatch); 12 thread.start(); 13 } 14 15 try 16 { 17 // 阻塞当前线程,直到倒数计数器倒数到0 18 countDownLatch.await(); 19 } 20 catch (InterruptedException e) 21 { 22 e.printStackTrace(); 23 } 24 25 long end = System.currentTimeMillis(); 26 System.out.println("子线程执行时长:" + (end - start)); 27 } 28 }