什么面试竟然要我使用多线程

307 阅读3分钟

多线程的练习(一)

前言

面试某公司,面试官问道:是否了解多线程?

我虽底气不足,但还是点了点头,心道,说不了解那就凉了,说了解还有一线生机,况且还背了不少面经,说不定还能回答不少。心中不禁偷笑,快问点我背到的,什么Thread 、Runnable、多线程的并发、线程池。

面试官:那好,既然了解,我们写一道题目吧。

我:纳尼?!

原题

顺序递增打印正整数,从1开始打印到100,中间换行分隔。不允许重复打印出相同的数字,比如打印结果里出现2536之类的。
使用三个线程AB、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,这些就放在下一篇再来讲