多线程的练习(一)
前言
面试某公司,面试官问道:是否了解多线程?
我虽底气不足,但还是点了点头,心道,说不了解那就凉了,说了解还有一线生机,况且还背了不少面经,说不定还能回答不少。心中不禁偷笑,快问点我背到的,什么Thread 、Runnable、多线程的并发、线程池。
面试官:那好,既然了解,我们写一道题目吧。
我:纳尼?!
原题
顺序递增打印正整数,从1开始打印到100,中间换行分隔。不允许重复打印出相同的数字,比如打印结果里出现2个5,3个6之类的。
使用三个线程A、B、C,其中线程A打印3的倍数,B打印5的倍数,C打印其他数字。
分析
冷静冷静,之前没写过多线程题目,需要冷静下来,先和面试官分析一下,显得自己还是会做的。
1、顺序打印,这就意味着,3个线程之间需要有执行的顺序,不然肯定是乱序
2、不允许重复的数字,说明线程需要判断当前的数字是否已经被打印,已打印就不在打印
嗯嗯,不愧是我,分析的有点道理,可是,还是没思路呀。
思路
不管了,硬着头皮说:
1、需要保证执行的顺序,所以需要锁来控制哪个线程该执行
2、还需要静态变量,来表示已打印到哪个数字
public static int cur = 0; // 表明目前已打印到哪个元素
public static ReentrantLock lock = new ReentrantLock(); // 表示锁,保证线程打印之间的串行,线程打印前,需要申请
得到一个简单的思路:每个线程,从1到100来进行打印,符合线程的要求,才被允许打印,比如线程A需要数字的3的倍数,进入线程打印,lock.lock() 获取打印权限,发现 cur==i-1 表明已打印i-1,下一个该打印就是i ,ok,那就打印i,再将cur++,再将lock.unlock()释放。如果cur!=i-1 将锁释放,并查看当前cur是否已经达到i,到了,就意味着该元素不用打印了。
程序
import java.lang.management.ThreadInfo;
import java.util.concurrent.locks.ReentrantLock;
/**
* 顺序递增打印正整数,从1开始打印到100,中间换行分隔。不允许重复打印出相同的数字,比如打印结果里出现2个5,3个6之类的。
*
* 要求如下:
* 1、使用三个线程A、B、C,其中线程A打印3的倍数,B打印5的倍数,C打印其他数字。
*/
public class T1 {
public static int cur = 0;
public static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
new Thread(()->{
for(int i=0;i<=100;i++){
if(i%3==0){
printNum(i);
}
}
},"Thread A").start();
new Thread(()->{
for(int i=0;i<=100;i++){
if(i%5==0){
printNum(i);
}
}
},"Thread B").start();
new Thread(()->{
for(int i=0;i<=100;i++){
if((i%3!=0)&&(i%5!=0)){
printNum(i);
}
}
},"Thread C").start();
}
public static void printNum(int i){
while(true){
lock.lock();
if(cur == i-1){
System.out.println(Thread.currentThread().getName()+" "+i);
cur++;
lock.unlock();
break;
}else{
lock.unlock();
if(cur >= i){
break;
}
}
}
}
}
问题
啊,总算实现题目的要求,不过小伙伴一看,就知道这样的写法存在的问题
-
比如,线程A,想要打印3,先获得资源,当时cur还是在0,又需要将资源释放,继续在while循环里,相当于一直在空转。
改进点
这里就在想,能不能让线程拿到执行到了该线程下一个需要打印的数字,就不在循环,直接wait在这,变成由上一个数字的打印线程执行后唤起该线程。
是否可以使用 AtomicInteger 来替代int。
ok,这些就放在下一篇再来讲