天天看点

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会大幅度提高程序的效率。

继续阅读