前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一道多线程协同面试题的三种解决方案

一道多线程协同面试题的三种解决方案

作者头像
冬天里的懒猫
发布2020-09-16 11:19:41
6380
发布2020-09-16 11:19:41
举报
文章被收录于专栏:做不甩锅的后端

最近看到一道线程协同的阿里面试题。

代码语言:javascript
复制
(JAVA)有3 个独立的线程,一个只会输出A,一个只会输出L,一个只会输出I。
在三个线程同时启动的情况下,请用合理的方式让他们按顺序打印ALIALI。
三个线程开始正常输出后,主线程若检测到用户任意的输入则停止三个打印线程的工作,整体退出。

这个题目实际上是在考察线程间协调。鉴于前面学习的线程间通信的三种方法,现在用三种方法来完成该问题。

1.synchronized 配合 wait/notify

首先用最经典的synchronized来实现。

代码语言:javascript
复制
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class ThreeThreadTest {

	private final static Object lock = new Object();

	private static volatile int count = 0;

	public static void main(String[] args) {

		Thread t1 = new Thread(() -> {
			try {
				while (true) {
					synchronized (lock) {
						lock.notifyAll();
						if (count % 3 == 0) {
							System.out.println(Thread.currentThread().getName()+":A");
							count++;
							TimeUnit.MILLISECONDS.sleep(500);
						}
						lock.wait();
					}
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		},"T1");

		Thread t2 = new Thread(() -> {
			try {
				while (true) {
					synchronized (lock) {
						lock.notifyAll();
						if (count % 3 == 1) {
							System.out.println(Thread.currentThread().getName()+":L");
							count++;
							TimeUnit.MILLISECONDS.sleep(500);
						}
						lock.wait();
					}
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		},"T2");

		Thread t3 = new Thread(() -> {
			try {
				while (true) {
					synchronized (lock) {
						lock.notifyAll();
						if (count % 3 == 2) {
							System.out.println(Thread.currentThread().getName()+":I");
							count++;
							TimeUnit.MILLISECONDS.sleep(500);
						}
						lock.wait();
					}
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		},"T3");

		t1.setDaemon(true);
		t2.setDaemon(true);
		t3.setDaemon(true);
		t1.start();
		t2.start();
		t3.start();

		Scanner scan = new Scanner(System.in);
		String input;
		do{
			input = scan.next();
		}while (input == null);
		System.out.println("exit");
		
	}
}

由于需要在输入的时候退出主线程,那么现在将其他三个线程都设置为守护线程。

代码语言:javascript
复制
T1:A
T2:L
T3:I
T1:A
T2:L
T3:I
T1:A
T2:L
T3:I
T1:A
T2:L
T3:I
T1:A
T2:L
T3:I
1
exit

此时输入1,导致所有线程全部退出。

2.ReentrantLock & Condation

代码语言:javascript
复制
public class ThreeThreadTest {

	private static final ReentrantLock lock = new ReentrantLock();

	private static final Condition con1 = lock.newCondition();
	private static final Condition con2 = lock.newCondition();
	private static final Condition con3 = lock.newCondition();

	private static int count = 0;

	public static void main(String[] args) throws InterruptedException{
		Thread t1 = new Thread(() -> {
			lock.lock();
			try {
				while (true) {
					if((count%3) == 0) {
						System.out.println(Thread.currentThread().getName()+":A");
						TimeUnit.MILLISECONDS.sleep(500);
						con2.signal();
						count ++;
					}else {
						con1.await();
					}
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally {
				lock.unlock();
			}
		});
		Thread t2 = new Thread(() -> {
			lock.lock();
			try {
				while (true) {
					if((count%3) == 1) {
						System.out.println(Thread.currentThread().getName()+":L");
						TimeUnit.MILLISECONDS.sleep(500);
						con3.signal();
						count ++;
					}else {
						con2.await();
					}
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally {
				lock.unlock();
			}
		});
		Thread t3 = new Thread(() -> {
			lock.lock();
			try {
				while (true) {
					if((count%3) == 2) {
						System.out.println(Thread.currentThread().getName()+":I");
						TimeUnit.MILLISECONDS.sleep(500);
						con1.signal();
						count ++;
					}else {
						con3.await();
					}
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally {
				lock.unlock();
			}
		});

		t1.setDaemon(true);
		t2.setDaemon(true);
		t3.setDaemon(true);

		t1.start();
		TimeUnit.MILLISECONDS.sleep(100);
		t2.start();
		TimeUnit.MILLISECONDS.sleep(100);
		t3.start();

		Scanner scan = new Scanner(System.in);
		String input;
		do{
			input = scan.next();
		}while (input == null);
		System.out.println("exit");
	}
}

执行结果:

代码语言:javascript
复制
Thread-0:A
Thread-1:L
Thread-2:I
Thread-0:A
Thread-1:L
Thread-2:I
1
exit

同理也可以解决这个问题。

3.LockSupport

代码语言:javascript
复制
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class ThreeThreadTest {

	private static boolean state = true;

	public static void main(String[] args) throws InterruptedException{

		Thread t1 = new Thread(() -> {
			while (true) {
				System.out.println(Thread.currentThread().getName() + ":A");
				LockSupport.park();
			}
		},"T1");
		Thread t2 = new Thread(() -> {
			while (true) {
				System.out.println(Thread.currentThread().getName() + ":L");
				LockSupport.park();
			}
		},"T2");
		Thread t3 = new Thread(() -> {
			while (true) {
				System.out.println(Thread.currentThread().getName() + ":I");
				LockSupport.park();
			}
		},"T3");

		Thread t4 = new Thread(() -> {
			int i=0;
			try {
				while (state) {
					if((i%3)==0) {
						LockSupport.unpark(t1);
					}else if((i%3)==1) {
						LockSupport.unpark(t2);
					}else {
						LockSupport.unpark(t3);
					}
					i++;
					TimeUnit.MILLISECONDS.sleep(500);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		});

		t1.setDaemon(true);
		t2.setDaemon(true);
		t3.setDaemon(true);
		t4.setDaemon(true);
		t1.start();
		TimeUnit.MILLISECONDS.sleep(100);
		t2.start();
		TimeUnit.MILLISECONDS.sleep(100);
		t3.start();
		TimeUnit.MILLISECONDS.sleep(100);
		t4.start();

		Scanner scan = new Scanner(System.in);

		String input;
		do{
			input = scan.next();
		}while (input == null);
	}
}

上述代码输出结果:

代码语言:javascript
复制
T1:A
T2:L
T3:I
T1:A
T2:L
T3:I
T1:A
T2:L
1

可以看到使用LockSupport,不需要使用锁进行配合。 这是最灵活的一种方式。

4.总结

对于线程同步的问题,通过前面的学习,实际上有三种方式可以实现。分别是:

  • synchronized + wait + notify/notifyAll
  • ReentrantLock + Condation:await/signal
  • LockSupport:park / unpark 以上三种方式都可以实现同步,我们可以根据实际需求可以选择合适的方式来实现。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/09/13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.synchronized 配合 wait/notify
  • 2.ReentrantLock & Condation
  • 3.LockSupport
  • 4.总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档