JAVA 多线程开篇 -从按顺序打印ABC开始

序言

  很想把一个问题弄清楚,特别是以前一直模模糊糊的并发编程,今天在华为OJ上碰到一道题,“顺序打印ABC的两种方法开始写起”,就以这道题开篇,希望日后有时间把并发编程的基本问题弄清楚。

问题

  启动三个线程,一个线程打印A,一个打印B,一个打印C,按顺序打印ABC....。如输入3,输出就是“ABCABCABC”

程序

  线程的调度是由系统操作的,要想多个线程按照要求顺序打印,就必须做好线程间的同步。

 思路:四个线程循环打印,但是一个线程打印一个字母释放锁后无法确定获得锁的是哪一个线程,这就需要用一个标志判断是否轮到自己打印,是的话就打印,然后让下一个线程线程打印,否则等待。

 主要有两种方法:

  1、使用synchronized关键字,以及对象的wait、notify和notifyAll方法

  2、使用Lock和Condition

  这里先给出第二种的程序,打印ABCD。

  1 import java.util.Scanner;
  2 import java.util.concurrent.locks.Condition;
  3 import java.util.concurrent.locks.Lock;
  4 import java.util.concurrent.locks.ReentrantLock;
  5 
  6 public class Main {
  7     public static void main(String[] args) throws Exception  
  8     {
  9         Scanner scanner=new Scanner(System.in);
 10         int n=scanner.nextInt();
 11         new Thread(new Printer('A', n)).start();
 12         new Thread(new Printer('B', n)).start();
 13         new Thread(new Printer('C', n)).start();
 14         new Thread(new Printer('D', n)).start();
 15         scanner.close();
 16     }
 17     
 18     static class Printer implements Runnable
 19     {
 20         //所有的线程操作都需要这把锁,所以必须是静态的,否则就不是同一对象了,锁的作用就没了。
 21         //可以把这个锁理解成synchronize的里面的对象锁
 22         public static Lock lock=new ReentrantLock();   (1)锁
 23         //Condition依赖于锁,并且可以获得多个Condition,能够精准地控制线程
 24         public static Condition conditionA=lock.newCondition();    (2)精准唤醒或者线程等待
 25         public static Condition conditionB=lock.newCondition();
 26         public static Condition conditionC=lock.newCondition();
 27         public static Condition conditionD=lock.newCondition();
 28         public static char last='D';     (3)判断本线程是否该await
 29         private char c;
 30         private int n;
 31         public Printer(char c,int n)
 32         {
 33             this.c=c;
 34             this.n=n;
 35         }
 36         @Override
 37         public void run() {
 38             // TODO Auto-generated method stub
 39             switch (c) {
 40             case 'A':
 41                     printA();
 42                 break;
 43             case 'B':
 44                     printB();
 45                 break;
 46             case 'C':
 47                     printC();
 48                 break;
 49             case 'D':
 50                     printD();
 51                 break;
 52             default:
 53                 break;
 54             }
 55         }
 56         
 57         private void printA()
 58         {
 59             for(int i=0;i<n;i++)
 60             {
 61                 lock.lock();
 62                 try {
 63                     if(last!='D')
 64                     {
 65                         conditionA.await();
 66                     }
 67                     System.out.print(c);//假如当前是D,那么下一个肯定就是A即自身,可以打印了
 68                     last=c;
 69                     conditionB.signal();
 70                 } catch (InterruptedException e) {
 71                     // TODO Auto-generated catch block
 72                     e.printStackTrace();
 73                 }
 74                 finally
 75                 {
 76                     lock.unlock();
 77                 }
 78             }
 79         }
 80         
 81         private void printB()
 82         {
 83             for(int i=0;i<n;i++)
 84             {
 85                 lock.lock();
 86                 try {
 87                     if(last!='A')
 88                     {
 89                         conditionB.await();
 90                     }
 91                     System.out.print(c);
 92                     last=c;
 93                     conditionC.signal();
 94                 } catch (InterruptedException e) {
 95                     // TODO Auto-generated catch block
 96                     e.printStackTrace();
 97                 }
 98                 finally
 99                 {
100                     lock.unlock();
101                 }
102             }
103         }
104         
105         private void printC()
106         {
107             for(int i=0;i<n;i++)
108             {
109                 lock.lock();
110                 try {
111                     if(last!='B')
112                     {
113                         conditionC.await();
114                     }
115                     System.out.print(c);
116                     last=c;
117                     conditionD.signal();
118                 } catch (InterruptedException e) {
119                     // TODO Auto-generated catch block
120                     e.printStackTrace();
121                 }
122                 finally
123                 {
124                     lock.unlock();
125                 }
126             }
127         }
128         
129         private void printD()
130         {
131             for(int i=0;i<n;i++)
132             {
133                 lock.lock();
134                 try {
135                     if(last!='C')
136                     {
137                         conditionD.await();
138                     }
139                     System.out.print(c);
140                     last=c;
141                     conditionA.signal();
142                 } catch (InterruptedException e) {
143                     // TODO Auto-generated catch block
144                     e.printStackTrace();
145                 }
146                 finally
147                 {
148                     lock.unlock();
149                 }
150             }
151         }
152         
153     }
154 }

     需要注意对Condition的理解,Condition不能从名字上判断跟哪个线程相关,比如ConditionA就与AThread相关,这种理解是错的,condition只与lock相关,conditionB.await()不能让BThread阻塞,而只是让当前线程阻塞并放弃锁,进入睡眠等待被唤醒,conditionA.await()的目的是阻塞放弃锁并可以通过conditionA.signal()唤醒(并不是立即唤醒,而是在unlock后)。整个程序的执行过程大概如下:

  假设ABCD四个线程同时执行,线程B发现上一个字母不是B则阻塞释放锁,CD同理,只有A能够运行打印A,然后唤醒B,以此类推,就不断地打印ABCDABCD。

  在网上看到另外一种,利用线程调度的随机性到了自己的就打印,没有到就释放锁,重新竞争,这样效率较低,不能控制,但是还是能够实现。

 

 1 import java.util.concurrent.locks.Lock;
 2 import java.util.concurrent.locks.ReentrantLock;
 3 
 4 public class Main {
 5     private static int state = 0;
 6 
 7     public static void main(String[] args) {
 8         final Lock l = new ReentrantLock();
 9         
10         Thread B = new Thread(new Runnable(){
11             @Override
12             public void run() {
13                 while (state<=30) {
14                     l.lock();
15                     if(state%3==1){
16                         System.out.print("B");
17                         state ++;
18                     }
19                     else {
20                         //System.out.println("NOT");
21                     }
22                     l.unlock();
23                 }
24             }
25         });
26         Thread A = new Thread(new Runnable(){
27             @Override
28             public void run() {
29                 while (state<=30) {
30                     l.lock();
31                     if(state%3==0){
32                         System.out.print("A");
33                         state ++;
34                     }
35                     else {
36                         //System.out.println("NOT");
37                     }
38                     l.unlock();
39                 }
40             }
41         });
42         Thread C = new Thread(new Runnable(){
43             @Override
44             public void run() {
45                 while (state<=30) {
46                     l.lock();
47                     if(state%3==2){
48                         System.out.print("C");
49                         state ++;
50                     }
51                     else {
52                         //System.out.println("NOT");
53                     }
54                     l.unlock();
55                 }
56             }
57         });
58         B.start();
59         C.start();
60         A.start();
61     }
62 
63 }
View Code

 

 

使用synchronized比较经典的方法

 1 package com.mythread.test;
 2 
 3 import java.util.concurrent.atomic.AtomicInteger;
 4 
 5 public class TestAsynTreadXunlei {
 6     public static void main(String argv[]) {
 7         
 8         AtomicInteger synObj = new AtomicInteger(0);
 9         
10         TestPrint a = new TestPrint(synObj, "A", 0);
11         TestPrint b = new TestPrint(synObj, "B", 1);
12         TestPrint c = new TestPrint(synObj, "C", 2);
13         
14         a.start();
15         b.start();
16         c.start();
17     }
18 }
19 
20 class TestPrint extends Thread {
21     
22     private AtomicInteger synObj;
23     private String name;
24     private int flag;
25     
26     private int count = 0;
27     
28     public TestPrint(AtomicInteger synObj, String name, int flag) {
29         this.synObj = synObj;
30         this.name = name;
31         this.flag = flag;
32     }
33     
34     @Override
35     public void run() {
36         while (true) {
37             synchronized (synObj) {
38                 if (synObj.get() % 3 == flag) {
39                     synObj.set(synObj.get() + 1);
40                     System.out.println(name);
41                     count++;
42                     synObj.notifyAll();
43                     if (count == 10) {
44                         break;
45                     }
46                 } else {
47                     try {
48                         synObj.wait();
49                     } catch (InterruptedException e) {
50                         // TODO Auto-generated catch block
51                         e.printStackTrace();
52                     }
53                 }
54             }
55         }
56     }
57 }

 

posted @ 2015-09-03 21:42  Maydow  阅读(1796)  评论(0编辑  收藏  举报