线程间通信实例之轮流打印ABC

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/J080624/article/details/82745727

线程间通信,就要联想到Object的wait、notify、notifyAll和Lock.new Condition()的await、signal、signalAll。

【1】需求一,三个线程顺序打印ABC,每个线程打印十次,ABC分别为线程名字

效果如下:

A
A
A
A
A
A
A
A
A
A
//BC依次排列

使用synchronized、wait、notifyAll实例代码如下:

public class TestSwapThread {
    public static void main(String[] args){
		//创建三把锁
        final Object obj1 = new Object();
        final Object obj2 = new Object();
        final Object obj3 = new Object();
        SwapThread swapThread = new SwapThread(obj1,obj2);
        SwapThread swapThread2 = new SwapThread(obj3,obj1);
        SwapThread swapThread3 = new SwapThread(obj2,obj3);
        swapThread.setName("A");
        swapThread.start();
        //这里来保证次序
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        swapThread2.setName("B");
        swapThread2.start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        swapThread3.setName("C");
        swapThread3.start();
    }
}

class SwapThread extends  Thread{

    private Object obj1 = null;
    private Object obj2 = null;

    public SwapThread(Object obj1,Object obj2){
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    @Override
    public void run() {
        synchronized (obj1){
            System.out.println(Thread.currentThread().getName()+"准备进入第二层");
            synchronized (obj2){
                for (int i=0;i<10;i++){
                    System.out.println(Thread.currentThread().getName()+" : "+i);
                }
         //nofityAll并不会释放锁,但是同步代码块执行完会自动释放锁
                obj2.notifyAll();
            }//这里释放内存锁
         //nofityAll并不会释放锁,但是同步代码块执行完会自动释放锁
            obj1.notifyAll();
            System.out.println(Thread.currentThread().getName()+" : end");
        }//这里释放外层锁
    }
}

使用lock实例如下

public class TestSwapThread3 {
    public static void main(String[] args){
    	//这里也可作为swapThread3 构造参数传入
        //Lock lock = new ReentrantLock();
        SwapThread3 swapThread3 = new SwapThread3();
        new Thread(swapThread3,"A").start();
        new Thread(swapThread3,"B").start();
        new Thread(swapThread3,"C").start();

    }
}

class SwapThread3 implements Runnable{
	//共享数据--同一把锁 ReentrantLock
    private Lock lock =new ReentrantLock() ;

    @Override
    public void run() {
	    //加锁
        lock.lock();
        try {
            int count =0;
            while (count<10){
                System.out.println(Thread.currentThread().getName()+" : "+count );
                count++;

            }
        }finally {
	        //释放锁
            lock.unlock();
        }

    }
}

程序执行结果如下:

这里写图片描述

其实lock加锁/释放锁和顺序打印本质是保存线程执行的次序,严格意义从理论来讲,第二种方式在某些情况下是不太正确的。


【2】需求二,三个线程轮流打印ABC,每个线程打印十次,ABC分别为线程名字

效果如下:

A
B
C
//....

这时,就对线程通信要求高了。


使用synchronized、wait、notifyAll实例代码如下:

public class TestSwapThread4 {
    public static void main(String[] args){

        final Object obj1 = new Object();
        final Object obj2 = new Object();
        final Object obj3 = new Object();
        SwapThread4 swapThread = new SwapThread4(obj1,obj2);
        SwapThread4 swapThread2 = new SwapThread4(obj2,obj3);
        SwapThread4 swapThread3 = new SwapThread4(obj3,obj1);
        swapThread.setName("A");
        swapThread.start();

        swapThread2.setName("B");
        swapThread2.start();

        swapThread3.setName("C");
        swapThread3.start();

    }
}

class SwapThread4 extends  Thread{

    private Object obj1 = null;
    private Object obj2 = null;

    public SwapThread4(Object obj1,Object obj2){
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    @Override
    public void run() {
        for (int i=0;i<10;i++){
            synchronized (obj1){
                synchronized (obj2){
                    System.out.println(Thread.currentThread().getName()+" : "+i);
                    //notifyAll并不会释放锁,代码块结束后释放锁
                    obj2.notifyAll();
                }
                try {
                    //避免程序不能正常结束
                    if (i==9){
                        obj1.notifyAll();
                    }else{
                    //wait方法将会释放锁标志,进入阻塞状态,等到唤醒
                        obj1.wait();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

测试结果如下:

这里写图片描述


使用Lock+Condition实例如下:

public class TestSwapThread2 {

    public static void main(String[] args){
        Lock lock = new ReentrantLock();
        Condition conditionA = lock.newCondition();
        Condition conditionB = lock.newCondition();
        Condition conditionC = lock.newCondition();
        SwapThread2 swapThread = new SwapThread2(lock,conditionA,conditionB);
        SwapThread2 swapThread2 = new SwapThread2(lock,conditionB,conditionC);
        SwapThread2 swapThread3 = new SwapThread2(lock,conditionC,conditionA);

        swapThread.setName("A");
        swapThread.start();

        swapThread2.setName("B");
        swapThread2.start();

        swapThread3.setName("C");
        swapThread3.start();

    }
}

class SwapThread2 extends Thread{

    private Lock lock ;
    private Condition condition1;
    private Condition condition2 ;

    public SwapThread2(Lock lock,Condition condition1,Condition condition2){
        this.lock = lock;
        this.condition1=condition1;
        this.condition2=condition2;
    }


    @Override
    public void run() {
  	  //加锁
        lock.lock();
        try {
            int count =0;
            while (count<10){
            //唤醒等待condition2的线程
                condition2.signal();
                System.out.println(Thread.currentThread().getName()+" : "+count );
                count++;
                if (count==10){
                //避免程序不能结束
                    condition1.signalAll();
                }else{
                    try {
                    	//当前线程进入阻塞,等到condition1被唤醒
                        condition1.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }finally {
            lock.unlock();
        }
    }
}

【3】需求三-如果每次ABC打印次数不一样且要交替进行呢?

实例代码如下:

public class TestABCAlternate {
	
	public static void main(String[] args) {
		AlternateDemo ad = new AlternateDemo();
		//创建线程A,循环20遍loopA
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 1; i <= 20; i++) {
					ad.loopA(i);
				}
			}
		}, "A").start();
		//创建线程B,循环20遍loopB
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 1; i <= 20; i++) {
					ad.loopB(i);
				}
			}
		}, "B").start();
			//创建线程C,循环20遍loopC
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 1; i <= 20; i++) {
					ad.loopC(i);
					System.out.println("-----------------------------------");
				}
			}
		}, "C").start();
	}
}

class AlternateDemo{
	//当前正在执行线程的标记
	private int number = 1; 
	//创建ReentrantLock实例
	private Lock lock = new ReentrantLock();
	//分别创建三个lock的Condition 实例
	private Condition condition1 = lock.newCondition();
	private Condition condition2 = lock.newCondition();
	private Condition condition3 = lock.newCondition();
	
	/**
	 * @param totalLoop : 循环第几轮
	 */
	public void loopA(int totalLoop){
		lock.lock();
		try {
			//1. 判断
			if(number != 1){
			//是当前线程阻塞,等到condition1被唤醒
				condition1.await();
			}
			//2. 打印
			for (int i = 1; i <= 1; i++) {
				System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
			}
			//3. 唤醒
			number = 2;
			condition2.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void loopB(int totalLoop){
		lock.lock();
		try {
			//1. 判断
			if(number != 2){
				condition2.await();
			}
			//2. 打印
			for (int i = 1; i <= 1; i++) {
				System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
			}			
			//3. 唤醒
			number = 3;
			condition3.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void loopC(int totalLoop){
		lock.lock();
		try {
			//1. 判断
			if(number != 3){
				condition3.await();
			}
			//2. 打印一遍
			for (int i = 1; i <= 1; i++) {
				System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
			}
			//3. 唤醒第一个线程 loopA 
			number = 1;
			condition1.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
}

Lock与Condition详解讲解参考博文:Lock与Condition使用讲解

展开阅读全文

三线程打印ABC的面试题

02-19

正确的代码是这样的:npublic class MutliThreadPrintABC implements Runnablenn //打印内容n String content;n //两个锁n Object prev;n Object self;n n public MutliThreadPrintABC(String content,Object prev,Object self) n this.content=content;n this.prev=prev;n this.self=self;n n n @Overriden public void run() n for(int i=0;i<10;i++)n synchronized (prev) n System.out.println(Thread.currentThread().getName()+"获取到了前一个线程的锁");n synchronized (self) n System.out.println(content);n self.notify();n n try n prev.wait();n catch (InterruptedException e) n e.printStackTrace();n n n n n n public static void main(String[] args) throws InterruptedException n Object a = new Object();n Object b = new Object();n Object c = new Object();n Thread A = new Thread(new MutliThreadPrintABC("A",c,a));n A.setName("A");n Thread B = new Thread(new MutliThreadPrintABC("B",a,b));n B.setName("B");n Thread C = new Thread(new MutliThreadPrintABC("C",b,c));n C.setName("C");n A.start();n Thread.sleep(100);n B.start();n Thread.sleep(100);n C.start();n nn我在学习这个的时候写了个错误的版本就是释放锁这一部分prev.wait()写到了最里面的同步块里:npublic void run() n for(int i=0;i<10;i++)n synchronized (prev) n System.out.println(Thread.currentThread().getName()+"获取到了前一个线程的锁");n synchronized (self) n System.out.println(content);n self.notify();n try n prev.wait();n catch (InterruptedException e) n e.printStackTrace();n n n n n n 这个时候程序运行会卡住,而且是A线程执行完就直接执行了C线程,而且都只打印了一次就卡住了,我实在搞不懂为什么B线程没拿到A线程执行完释放的锁,这个错误版本到底程序是怎么执行的,本人小白没干过多线程的工作,正在学习,求大佬解释一下 问答

没有更多推荐了,返回首页