以前把thread基础温习了一遍,而后想经过作一些题目来加深本身的印象,恰好大四找工做面试时被问到一道题目:A-Z,1-26,使用多线程打印出A1B2C3D4E5......这种,当时本身好像回答的并很差,因而今天从新写了一遍。面试
首先咱们须要新建两个类,一个用来打印A-Z,一个用来打印1-26。这里为了省略,我只打印了前面几个数字和字母。咱们一步一步来。多线程
public class PrintCharacter extends Thread { @Override public void run() { List<String> list = Arrays.asList("A", "B", "C", "D", "E"); for (String str : list) { System.out.println(str); } } }
public class PrintNumber extends Thread { @Override public void run() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); for (Integer i : list) { System.out.println(i); } } }
public static void main(String[] args) { PrintCharacter pc = new PrintCharacter(); PrintNumber pn = new PrintNumber(); pc.setPriority(10); pn.setPriority(1); pc.start(); pn.start(); }
这里的代码比较简单,主线程开启了两个线程,并设置打印字母的线程的优先级为最高(网上说设置优先级并不能必定保证会先执行,可是我这里试验过不少次都是先打印字母的)。经过代码咱们看出不出意外结果应该是ABCDE12345这样打印的。不符合题目。而后我想到了sleep()这个方法,他可让线程休眠必定时间再进入就绪状态,那么理论上他是彻底能够实现题目的效果的。ide
public class PrintCharacter extends Thread { @Override public void run() { List<String> list = Arrays.asList("A", "B", "C", "D", "E"); for (String str : list) { try { System.out.println(str); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
public class PrintNumber extends Thread { @Override public void run() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); for (Integer i : list) { try { System.out.println(i); Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
这里与上面代码的区别就是每次循环打印以后我让线程休眠0.5s,首先打印字母A和数字1,而后两个线程休眠0.5s,而后继续打印,直到循环结束。运行结果证实这种方法的确可行,可是这种方法只能说完成了题目要求的。他须要线程一直休眠。这种实现方法我的以为能够帮助咱们理解sleep()方法,可是并很差。这个时候我想到了另外的一种实现方法,那就是多线程中最经常使用的锁,经过锁来实现交替打印。在说这种方法以前咱们再次说明wait()方法和notify()方法。this
wait()方法和notify()方法的使用前提都须要拿到锁,所以他们一般在同步代码块中使用。wait()方法会释放当前线程的锁,而后自身进入阻塞状态。notify()方法会唤醒一个正在阻塞状态的线程,而后他不会当即释放锁,而是等到执行完同步代码块中的内容,在释放锁。而后就是wait()方法和notify()方法的顺序不能错,即先执行wait()方法在执行notify()方法才能被唤醒。介绍完这两个方法,而后咱们开始看代码。spa
public class PrintCharacter extends Thread { private Object lock; public PrintCharacter(Object lock) { this.lock = lock; } @Override public void run() { List<String> list = Arrays.asList("A", "B", "C", "D", "E"); for (String str : list) { synchronized (lock) { try { lock.notify(); System.out.println(str); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
public class PrintNumber extends Thread { private Object lock; public PrintNumber(Object lock) { this.lock = lock; } @Override public void run() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); for (Integer i : list) { synchronized (lock) { try { lock.notify(); System.out.println(i); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
public static void main(String[] args) { Object lock = 0; PrintCharacter pc = new PrintCharacter(lock); PrintNumber pn = new PrintNumber(lock); pc.setPriority(10); pn.setPriority(1); pc.start(); pn.start(); }
首先,咱们分析一下代码,这两个线程都有一个lock对象,并且经过synchronized关键字保证这个对象不能同时被多个线程访问。而后在run()方法中,首先唤醒一个正在阻塞状态的线程,而后打印。而后让释放锁并让自身阻塞。主线程运行时执行两个子线程,首先执行打印字母的线程,他执行notify()方法,由于当前没有线程处于阻塞状态,因此不起做用,而后打印A,以后释放锁,并让自身阻塞。而后打印数字的线程得到锁开始执行,他执行notify()方法唤醒打印字母的线程,而后打印1,而后让自身阻塞,最后释放锁。这样一直循环。直到循环结束。线程
这种方法看上去挺好的,可是实际上仍是有问题的,问题就是当两个线程最后一次循环的时候打印数字线程会调用wait()方法令自身阻塞。而后致使程序没法结束,,,这样看起来问题好像更严重了。。。既然打印数字得线程最后会阻塞,那么咱们将它得wait()方法改成wait(1000),这样当最后一次它阻塞自身1s后,就会从新就如就绪状态执行完run()方法。这样程序就能正常退出了。code