转自:详解使用synchronized解决三个线程依次轮流打印出75个数
原帖见:http://www.iteye.com/topic/1117703
问题描述:
一个关于线程的经典面试题,要求用三个线程,按顺序打印1,2,3,4,5.... 71,72,73,74, 75.
线程1先打印1,2,3,4,5, 然后是线程2打印6,7,8,9,10, 然后是线程3打印11,12,13,14,15. 接着再由线程1打印16,17,18,19,20....以此类推, 直到线程3打印到75。
直接上代码:
- package concurrent.test;
-
-
-
-
-
-
-
-
-
- public class Print1to75 {
- static class Printer implements Runnable{
- static int num = 1;
- static final int END = 75;
- int id;
-
- public Printer(int id) {
- this.id = id;
- }
-
- @Override
- public void run(){
- synchronized (Printer.class) {
- while(num <= END){
- if(num / 5 % 3 == id){
- System.out.print(id + ":");
- for(int i = 0; i < 5; i++){
- System.out.print(num++ + ", ");
- }
- System.out.println();
-
- Printer.class.notifyAll();
- }else{
- try {
- Printer.class.wait();
- } catch (InterruptedException e) {
- System.out.println("id" + "被打断了");
- }
- }
- }
- }
- }
- }
-
-
- public static void main(String[] args) {
-
- new Thread( new Printer(0)).start();
- new Thread( new Printer(1)).start();
- new Thread( new Printer(2)).start();
- }
- }
注释中说的也很明白,有问题欢迎大家讨论。
结果(运行了N次,结果都是一致的,请大家检验):
- 0:1, 2, 3, 4, 5,
- 1:6, 7, 8, 9, 10,
- 2:11, 12, 13, 14, 15,
- 0:16, 17, 18, 19, 20,
- 1:21, 22, 23, 24, 25,
- 2:26, 27, 28, 29, 30,
- 0:31, 32, 33, 34, 35,
- 1:36, 37, 38, 39, 40,
- 2:41, 42, 43, 44, 45,
- 0:46, 47, 48, 49, 50,
- 1:51, 52, 53, 54, 55,
- 2:56, 57, 58, 59, 60,
- 0:61, 62, 63, 64, 65,
- 1:66, 67, 68, 69, 70,
- 2:71, 72, 73, 74, 75,
注意第23行的synchronized (Printer.class) ,为什么是Printer.class,而不是this呢?
是因为Print.class也是一个对象,在当前JVM中是唯一的,它相当于一个“公证人”,三个线程竞争资源的时候都是从唯一的这个“公证人”手里拿到许可,才能进入synchronized体。
而如果是synchronized (this)的话,this也相当于一个“公证人”,那么三个线程各自有一个“公证人”,相当于各干各的,三个中间没有竞争关系,构不成同步。
可见只要是这三个的“公证人”是同一个家伙,就能保持同步,稍微修改一下代码,我们给三个线程传进去同一个“公证人”(其实就是一个普通的不能再普通的对象):
- package concurrent.test;
-
-
-
-
-
-
-
-
-
- public class Print1to75 {
- static class Printer implements Runnable{
- static int num = 1;
- static final int END = 75;
- int id;
- Object o;
-
- public Printer(int id, Object o) {
- this.id = id;
- this.o = o;
- }
-
- @Override
- public void run(){
- synchronized (o) {
- while(num <= END){
- if(num / 5 % 3 == id){
- System.out.print(id + ":");
- for(int i = 0; i < 5; i++){
- System.out.print(num++ + ", ");
- }
- System.out.println();
-
- o.notifyAll();
- }else{
- try {
- o.wait();
- } catch (InterruptedException e) {
- System.out.println("id" + "被打断了");
- }
- }
- }
- }
- }
- }
-
-
- public static void main(String[] args) {
-
- Object o = new Object();
- new Thread( new Printer(0, o)).start();
- new Thread( new Printer(1, o)).start();
- new Thread( new Printer(2, o)).start();
- }
- }
在第16行,添加了三个线程的”公证人“ Object o;
第25,34,37行都由原来的Printer.class改为了o;
在第50行,创建了一个Object对象,传给了三个线程。
运行结果和上面的是一模一样地!
- new Thread( new Printer(0, o)).start();
- new Thread( new Printer(1, o)).start();
- new Thread( new Printer(2, o)).start();
如果觉得这段不太优雅,可以使用ExecuorService来实现,道理是一样的。