经典面试题
下面是多线程顺序打印的经典面试题
1.三个线程分别打印 A,B,C,要求这三个线程一起运行,打印 n 次,输出形如“ABCABCABC…”的字符串
2.两个线程交替打印 0~100 的奇偶数
3.通过 N 个线程顺序循环打印从 0 至 100
4.多线程按顺序调用,A->B->C,AA 打印 5 次,BB 打印10 次,CC 打印 15 次,重复 10 次
5.用两个线程,一个输出字母,一个输出数字,交替输出 1A2B3C4D…26Z
6.利用两个线程,分别计算 0-n/2 和 n/2-n的和,然后返回和
思路:要么控制线程顺序,要么利用条件竞争锁
Semaphore
public static void main(String[] args) {
Semaphore s1 = new Semaphore(1);
Semaphore s2 = new Semaphore(0);
Semaphore s3 = new Semaphore(0);
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
s1.acquire();
System.out.println("A");
s2.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
s2.acquire();
System.out.println("B");
s3.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t3 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
s3.acquire();
System.out.println("C");
s1.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
t2.start();
t3.start();
}
第一题
public static void test(int nums){
Semaphore s1 = new Semaphore(1);
Semaphore s2 = new Semaphore(0);
AtomicInteger x = new AtomicInteger(0);
Thread t1 = new Thread(() -> {
try {
for (int i = 0; i < nums/2; i++) {
s1.acquire();
System.out.println("线程1 " + x);
x.getAndIncrement();
s2.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
try {
for (int i = 0; i < nums/2; i++) {
s2.acquire();
System.out.println("线程2 " + x);
x.getAndIncrement();
s1.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
t2.start();
}
第二题
使用 wait/notify
public class BBB {
Object object = new Object();
public int state;
public int times;
public BBB(int times) {
this.times = times;
}
public void printABC(String name,int currentstate){
for (int i = 0; i < times; i++)
synchronized (object) {
while (state % 3 != currentstate) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
state++;
System.out.print(name);
object.notifyAll();
}
}
public static void main(String[] args) {
BBB bbb = new BBB(10);
new Thread(()->{
bbb.printABC("A",0);
}).start();
new Thread(()->{
bbb.printABC("B",1);
}).start();
new Thread(()->{
bbb.printABC("C",2);
}).start();
}
}
第一题
思路:还是以第一题为例,我们用对象监视器来实现,通过 wait 和 notify() 方法来实现等待、通知的逻辑,A 执行后,唤醒 B,B 执行后唤醒 C,C 执行后再唤醒 A,这样循环的等待、唤醒来达到目的。
public class OddEvenPrinter {
private Object object = new Object();
private final int limit;
private volatile int count;
OddEvenPrinter(int initCount, int times) {
this.count = initCount;
this.limit = times;
}
private void print() {
synchronized (object) {
while (count < limit){
try {
System.out.println(String.format("线程[%s]打印数字:%d", Thread.currentThread().getName(), ++count));
object.notifyAll(); //通知宁外一个线程进入抢锁状态
object.wait(); //释放锁,此线程进入等待队列
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//防止有子线程被阻塞未被唤醒,导致主线程不退出
object.notifyAll();
}
}
public static void main(String[] args) {
OddEvenPrinter printer = new OddEvenPrinter(0, 100);
new Thread(printer::print, "奇数").start();
new Thread(printer::print, "偶数").start();
}
}
第二题
思路:使用对象监视器实现,两个线程 A、B 竞争同一把锁,只要其中一个线程获取锁成功,就打印 ++i,并通知另一线程从等待集合中释放,然后自身线程加入等待集合并释放锁即可。
使用Lock(ReentrantLock)
package main.java.org.多线程Demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @param
* @author XiaoSuDa
* @description: $
* @return $
* @throws
* @date $ $
*/
public class PrinterABC {
private int times; // 控制打印次数
private int state; // 当前状态值:保证三个线程之间交替打印
private Lock lock = new ReentrantLock();
public PrinterABC(int times) {
this.times = times;
}
public void print(String name,int currentState){
for (int i = 0; i < times; ) {
lock.lock();
if (state % 3 == currentState){
state++;
i++;
System.out.print(name);
}
lock.unlock();
}
}
public static void main(String[] args) {
//顺序打印10次
PrinterABC printerABC = new PrinterABC(10);
new Thread(() -> {
printerABC.print("A", 0);
}, "A").start();
new Thread(() -> {
printerABC.print("B", 1);
}, "B").start();
new Thread(() -> {
printerABC.print("C", 2);
}, "C").start();
}
}
第一题
思路:使用一个取模的判断逻辑 C%M ==N,题为 3 个线程,所以可以按取模结果编号:0、1、2,他们与 3 取模结果仍为本身,则执行打印逻辑。
CountDownLatch
public class cu {
public static void main(String[] args) throws InterruptedException {
int n = 100;
CountDownLatch count = new CountDownLatch(2);
AddTest test1 = new AddTest(0, n / 2, count);
AddTest test2 = new AddTest((n / 2) + 1, n, count);
Thread t1 = new Thread(test1, "t1");
Thread t2 = new Thread(test2, "t2");
t1.start();
t2.start();
count.await();
System.out.println("结果是:"+test1.getRes()+test2.getRes());
}
}
class AddTest implements Runnable{
private int begin;
private int end;
private int res;
private CountDownLatch count;
public AddTest(int begin, int end,CountDownLatch countDownLatch) {
this.begin = begin;
this.end = end;
this.count = countDownLatch;
}
@Override
public void run() {
int sum = 0;
for (int i = begin; i <= end; i++) {
sum+=i;
}
res = sum;
count.countDown(); //每次调用一个线程
}
public int getRes(){
return res;
}
}
第六题
思路:创建两个线程,分别计算,利用countdownlunch,进行线程同步,然后返回