天天看點

Java多線程——三個多線程案例總結

Java多線程——三個多線程案例總結

  非常經典的三道多線程案例

  1、寫兩個線程,一個線程列印1-52,另一個線程列印A~Z,

    列印順序為:12A34B…5152Z

  2、編寫一個程式,啟動三個線程,三個線程的名稱分别是A,B,C

    每個線程将自己的名稱在螢幕上列印5遍,列印順序是ABCABCABC…

  3、寫3個線程,列印如下:

     *Thread-0

     *[email protected]

     *[email protected]#Thread-2

     *[email protected]#Thread-2Thread-0

     *[email protected]#Thread-2[email protected]

     *[email protected]#Thread-2*[email protected]#Thread-2

     …

  仔細觀察這三道題,就會發現這三道題其實是同一類型的題目,都是多個線程按照一定的順序列印一些内容,解決多線程問題的關鍵是使用多線程的同步機制,而解決順序問題的關鍵是使用“信号量”機制(作業系統中)

  1、多線程的同步機制采用synchronized鎖機制,使用同步方法或同步代碼塊即可完成對多個線程通路共享内容的同步。

  2、“信号量”機制

    首先引入“信号量”的概念
  信号量(Semaphore),有時被稱為信号燈,是在多線程環境下使用的一種設施,是可以用來保證兩個或多個關鍵代碼段不被并發調用。在進入一個關鍵代碼段之前,線程必須擷取一個信号量;一旦該關鍵代碼段完成了,那麼該線程必須釋放信号量。其它想進入該關鍵代碼段的線程必須等待直到第一個線程釋放信号量。為了完成這個過程,需要建立一個信号量VI,然後将Acquire Semaphore VI以及Release Semaphore VI分别放置在每個關鍵代碼段的首末端。确認這些信号量VI引用的是初始建立的信号量。
    Java實作“信号量”

    讓多個線程調用同一個線程類的不同的方法,而在這個線程類中設定一個标志量(flag),由flag的初始化及重新指派來控制第一個線程的選擇以及不同線程對應的不同方法的執行順序。

   該類型題的關鍵就在于怎樣設定“信号量”來確定順序,也需要采用多線程的同步來避免産生“髒資料”。

   題解1:

package com.xiaoaxiao.test.thread_test.thread_practice;
		
		/**
		 * Created by xiaoaxiao on 2019/7/15
		 * Description: 寫兩個線程,一個線程列印1~52,另一個線程列印A~Z,
		 *              列印順序為:12A34B...5152Z
		 */
		
		class MyThread implements Runnable{
		
		    private int flag = 1;
		    private int count = 1;
		
		    @Override
		    public void run() {
		        if(flag == 1){
		            printNum();
		        }else {
		            printLetter();
		        }
		    }
		
		    public synchronized void printNum(){
		        while (flag != 1){
		            try {
		                wait();
		            } catch (InterruptedException e) {
		                e.printStackTrace();
		            }
		        }
		
		        System.out.print(2*count-1);
		        System.out.print(2*count);
		
		        flag = 2;
		        notify();
		    }
		
		    public synchronized void printLetter(){
		        while (flag != 2){
		            try {
		                wait();
		            } catch (InterruptedException e) {
		                e.printStackTrace();
		            }
		        }
		
		        System.out.print((char)((count-1)+'A'));
		        count++;
		
		        flag = 1;
		        notify();
		    }
		}
		
		public class ThreadPractice1 {
		
		    public static void main(String[] args) {
		        MyThread mt = new MyThread();
		
		        new Thread(()->{
		           for (int i=0;i<26;i++){
		               mt.printNum();
		           }
		        }).start();
		
		        new Thread(()->{
		            for(int i=0;i<26;i++){
		                mt.printLetter();
		            }
		        }).start();
		    }
		}
           
    注意:count++的位置應該是在一個完整的過程結束後執行,通過Lambda表達式的形式建立線程并執行。

   題解2:

package com.xiaoaxiao.test.thread_test.thread_practice;
		
		/**
		 * Created by xiaoaxiao on 2019/7/15
		 * Description: 編寫一個程式,啟動三個線程,三個線程的名稱分别是A,B,C
		 *              每個線程将自己的名稱在螢幕上列印5遍,列印順序是ABCABC
		 */
		class Print{
		
		    private int flag = 1;
		    private int count = 0;
		
		    public int getCount() {
		        return count;
		    }
		
		    public synchronized void printA(){
		        while (flag != 1){
		            try {
		                wait();
		            } catch (InterruptedException e) {
		                e.printStackTrace();
		            }
		        }
		
		        System.out.print(Thread.currentThread().getName());
		
		        count++;
		        flag = 2;
		        notifyAll();
		    }
		
		    public synchronized void printB(){
		        while (flag != 2){
		            try {
		                wait();
		            } catch (InterruptedException e) {
		                e.printStackTrace();
		            }
		        }
		
		        System.out.print(Thread.currentThread().getName());
		
		        count++;
		        flag = 3;
		        notifyAll();
		    }
		
		    public synchronized void printC(){
		        while (flag != 3){
		            try {
		                wait();
		            } catch (InterruptedException e) {
		                e.printStackTrace();
		            }
		        }
		
		        System.out.print(Thread.currentThread().getName());
		
		        count++;
		        flag = 1;
		        notifyAll();
		    }
		}
		
		class MyThread3 implements Runnable{
		
		    Print print = new Print();
		
		    @Override
		    public void run() {
		        while (this.print.getCount()<16){
		            if(Thread.currentThread().getName().equals("A")){
		                print.printA();
		            }else if(Thread.currentThread().getName().equals("B")){
		                print.printB();
		            }else {
		                print.printC();
		            }
		        }
		    }
		}
		
		public class ThreadPractice2 {
		
		    public static void main(String[] args) {

		        MyThread3 mt = new MyThread3();
		
		        Thread threadA = new Thread(mt,"A");
		        Thread threadB = new Thread(mt,"B");
		        Thread threadC = new Thread(mt,"C");
		
		        threadA.start();
		        threadB.start();
		        threadC.start();
		    }
		}

           
    注意:使用Print類隻是對列印資訊的封裝,也可以将這些資訊直接寫線上程類中,但沒有達到不同類“各司其職”的效果

   題解3:

package com.xiaoaxiao.test.thread_test.thread_practice;
		
		/**
		 * Created by xiaoaxiao on 2019/7/15
		 * Description: 寫3個線程,列印如下(利用SB,線程安全)
		 * *Thread-0
		 * *[email protected]
		 * *[email protected]#Thread-2
		 * *[email protected]#Thread-2*Thread-0
		 * *[email protected]#Thread-2*[email protected]
		 * [email protected]#Thread-2*[email protected]#Thread-2
		 * ...
		 */
		
		class Print2 {
		    private int flag = 1;
		    private int count = 0;
		//    private StringBuffer sb = new StringBuffer();
		    private StringBuilder sb = new StringBuilder();
		
		    public int getCount() {
		        return count;
		    }
		
		    public synchronized void printThread0() {
		        while (flag != 1) {
		            try {
		                wait();
		            } catch (InterruptedException e) {
		                e.printStackTrace();
		            }
		        }
		
		        sb.append("*").append(Thread.currentThread().getName());
		        System.out.println(sb);
		
		        flag = 2;
		        count++;
		        notifyAll();
		    }
		
		    public synchronized void printThread1() {
		        while (flag != 2) {
		            try {
		                wait();
		            } catch (InterruptedException e) {
		                e.printStackTrace();
		            }
		        }
		
		        sb.append("@").append(Thread.currentThread().getName());
		        System.out.println(sb.toString());
		
		        flag = 3;
		        count++;
		        notifyAll();
		    }
		
		    public synchronized void printThread2() {
		        while (flag != 3) {
		            try {
		                wait();
		            } catch (InterruptedException e) {
		                e.printStackTrace();
		            }
		        }
		
		        sb.append("#").append(Thread.currentThread().getName());
		        System.out.println(sb.toString());
		
		        flag = 1;
		        count++;
		        notifyAll();
		    }
		}
		
		class MyThread4 implements Runnable {
		
		    Print2 print2 = new Print2();
		
		    @Override
		    public void run() {
		        while (this.print2.getCount() < 31) {
		            if (Thread.currentThread().getName().equals("Thread-0")) {
		                print2.printThread0();
		            } else if (Thread.currentThread().getName().equals("Thread-1")) {
		                print2.printThread1();
		            } else {
		                print2.printThread2();
		            }
		        }
		    }
		}
		
		public class ThreadPractice3 {
		
		    public static void main(String[] args) {
		        MyThread4 mt = new MyThread4();
		        Thread thread0 = new Thread(mt);
		        Thread thread1 = new Thread(mt);
		        Thread thread2 = new Thread(mt);
		
		        thread0.start();
		        thread1.start();
		        thread2.start();
		    }
		}

           
    注意:可以使用StringBuffer也可以使用StringBuilder,雖然StringBuffer是線程安全的,但是這裡通路的已經是同步方法了,是以沒有必要用StringBuffer,使用StringBuilder會大幅度提高程式的效率。

繼續閱讀