1、按序输出
同时启动三个线程,分别为A、B、C,其中A线程打印“a”,B线程打印“b”,C线程打印“c”,要求按照abc顺序打印输出。
第一种方式
使用join实现,join方法表示调用线程必须等待该线程执行完成后才能继续往下执行。
public class SortPrint {
public static void main(String[] args) throws InterruptedException {
SortPrint sortPrint = new SortPrint();
Thread ta = new Thread(() -> sortPrint.A());
Thread tb = new Thread(() -> sortPrint.B());
Thread tc = new Thread(() -> sortPrint.C());
ta.start();
ta.join();
tb.start();
tb.join();
tc.start();
}
public void A() {
System.out.println("a");
}
public void B() {
System.out.println("b");
}
public void C() {
System.out.println("c");
}
}
实际上join方法并不满足同时启动三个线程的要求,不过join方法是保证线程执行顺序的一种简单的方式,所以还是拿出来演示一下。
第二种方式
使用同步的方式,通过notifyAll和wait方法实现线程间的通信。
public class SortPrint_V2 {
static String flag = "A";
public static void main(String[] args) {
SortPrint_V2 sortPrint_v2 = new SortPrint_V2();
Thread ta = new Thread(() -> sortPrint_v2.A());
Thread tb = new Thread(() -> sortPrint_v2.B());
Thread tc = new Thread(() -> sortPrint_v2.C());
ta.start();
tb.start();
tc.start();
}
public synchronized void A() {
try {
while (true) {
if (flag.equals("A")) {
System.out.println("a");
flag = "B";
this.notifyAll();
} else {
this.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void B() {
try {
while (true) {
if (flag.equals("B")) {
System.out.println("b");
flag = "C";
this.notifyAll();
} else {
this.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void C() {
try {
while (true) {
if (flag.equals("C")) {
System.out.println("c");
flag = "A";
this.notifyAll();
} else {
this.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2、交替打印
同时启动两个线程,分别为A、B,其中A线程打印“a”,B线程打印“b”,要求按照ababab…的顺序交替打印输出。
第一种方式
使用Semaphore的方式,先给A线程初始化一个通行证,A线程输出完之后给B线程一个通行证,B线程输出完之后再给A一个通行证,以此反复,实现需求。
public class TurnPrint {
Semaphore semaphore1 = new Semaphore(1);
Semaphore semaphore2 = new Semaphore(0);
public static void main(String[] args) {
TurnPrint turnPrint = new TurnPrint();
Thread ta = new Thread(() -> turnPrint.A());
Thread tb = new Thread(() -> turnPrint.B());
ta.start();
tb.start();
}
public void A() {
try {
while (true) {
semaphore1.acquire();
System.out.println("a");
semaphore2.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void B() {
try {
while (true) {
semaphore2.acquire();
System.out.println("b");
semaphore1.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
第二种方式
和第一道题一样,使用notifyAll和wait实现。
public class TurnPrint_V2 {
static String flag = "A";
public static void main(String[] args) {
TurnPrint_V2 turnPrint = new TurnPrint_V2();
Thread ta = new Thread(() -> turnPrint.A());
Thread tb = new Thread(() -> turnPrint.B());
ta.start();
tb.start();
}
public synchronized void A() {
try {
while (true) {
if (flag.equals("A")) {
System.out.println("a");
flag = "B";
this.notify();
} else {
this.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void B() {
try {
while (true) {
if (flag.equals("B")) {
System.out.println("b");
flag = "A";
this.notify();
} else {
this.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
以上两道题基本上一个意思,几种方法都能实现这两个需求。
实现生产者与消费者
1、notifyAll和wait实现版
public class ProducerAndConsumer {
public static void main(String[] args) {
//让生产者、消费者共享同一个商品工厂
GoodsFactory goodsFactory = new GoodsFactory();
Producer producer = new Producer(goodsFactory);
producer.start();
Consumer consumer = new Consumer(goodsFactory);
consumer.start();
}
}
/**
* 商品工厂,负责生产和消费商品
*/
class GoodsFactory {
AtomicInteger atomicInteger = new AtomicInteger();
List<Goods> goodsList = new ArrayList<>();
public synchronized void consumerGoods() throws InterruptedException {
if (goodsList.size() <= 0) {
System.out.println("商品消费完了,等待生产!");
//商品消费完了以后,释放锁并阻塞自己,等待唤醒的通知。
this.wait();
} else {
Goods goods = goodsList.remove(0);
System.out.println("消费了一个商品:" + goods);
//消费了一个商品后,唤醒生产者
this.notifyAll();
}
}
public synchronized void producerGoods() throws InterruptedException {
if (goodsList.size() >= 5) {
System.out.println("商品太多了,停止生产,等待被消费!");
//商品满了以后,释放锁并阻塞自己,等待唤醒的通知。
this.wait();
} else {
Goods goods = new Goods(atomicInteger.getAndIncrement(), "xxoo");
goodsList.add(goods);
System.out.println("生产了一个商品:" + goods);
//生产了一个商品后,唤醒消费者
this.notifyAll();
}
}
}
/**
* 消费者线程,调用商品工厂消费商品
*/
class Consumer extends Thread {
GoodsFactory goodsFactory;
public Consumer(GoodsFactory goodsFactory) {
this.goodsFactory = goodsFactory;
}
@Override
public void run() {
try {
while (true) {
Thread.sleep(new Random().nextInt(10) * 100);
goodsFactory.consumerGoods();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 生产者线程,调用商品工厂生产商品
*/
class Producer extends Thread {
GoodsFactory goodsFactory;
public Producer(GoodsFactory goodsFactory) {
this.goodsFactory = goodsFactory;
}
@Override
public void run() {
try {
while (true) {
Thread.sleep(new Random().nextInt(10) * 100);
goodsFactory.producerGoods();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Goods {
private int goodId;
private String goodName;
public Goods(int goodId, String goodName) {
this.goodId = goodId;
this.goodName = goodName;
}
@Override
public String toString() {
return "Goods{" +
"goodId=" + goodId +
", goodName='" + goodName + '\'' +
'}';
}
}
2、基于阻塞队列BlockingDeque实现版
完全依赖阻塞队列自身的API帮我们实现,非常简单。
只需替换上一版的GoodsFactory ,其他不变
class GoodsFactory {
AtomicInteger atomicInteger = new AtomicInteger();
LinkedBlockingDeque<Goods> goodsList = new LinkedBlockingDeque<>(5);
public void consumerGoods() throws InterruptedException {
Goods goods = goodsList.take();
System.out.println("消费了一个商品:" + goods + ",当前还有" + goodsList.size() + "件商品");
}
public void producerGoods() throws InterruptedException {
Goods goods = new Goods(atomicInteger.getAndIncrement(), "xxoo");
goodsList.put(goods);
System.out.println("生产了一个商品:" + goods + ",当前还有" + goodsList.size() + "件商品");
}
}
4、哲学家进餐
第一位哲学家和第二位哲学家 共享1号叉子。
第二位哲学家和第三位哲学家 共享2号叉子。
第三位哲学家和第四位哲学家 共享3号叉子。
第四位哲学家和第五位哲学家 共享4号叉子。
第五位哲学家和第一位哲学家 共享5号叉子。
哲学家进餐是一个经典的描述多线程情况下出现死锁的问题,比如下面这段代码按照正常的逻辑实现,就会有死锁问题的产生。
public class DiningPhilosophers {
public static void main(String[] args) {
Fork f1 = new Fork("1");
Fork f2 = new Fork("2");
Fork f3 = new Fork("3");
Fork f4 = new Fork("4");
Fork f5 = new Fork("5");
new Thread(new Philosophers("1号哲学家", f1, f5)).start();
new Thread(new Philosophers("2号哲学家", f2, f1)).start();
new Thread(new Philosophers("3号哲学家", f3, f2)).start();
new Thread(new Philosophers("4号哲学家", f4, f3)).start();
new Thread(new Philosophers("5号哲学家", f5, f4)).start();
}
}
class Philosophers extends Thread {
private Fork leftFork;
private Fork rightFork;
private String name;
public Philosophers(String name, Fork leftFork, Fork rightFork) {
this.name = name;
this.leftFork = leftFork;
this.rightFork = rightFork;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
dining();
thinking();
}
}
private void thinking() {
System.out.println(name + "正在思考");
}
private void dining() {
synchronized (leftFork) {
System.out.println(name + leftFork);
synchronized (rightFork) {
System.out.println(name + rightFork);
System.out.println(name + "正在用餐");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class Fork {
private String name;
public Fork(String name) {
this.name = name;
}
@Override
public String toString() {
return "Fork{" +
"name='" + name + '\'' +
'}';
}
}
每个哲学家都拿起了自己左边的叉子,并等待右边的叉子。。。
要解决的死锁的问题一般只要打破产生死锁的4个必要条件的其中一个即可解决。理解死锁产生的四个必要条件
1、通过一个助理类辅助实现,破坏死锁中请求不释放的条件。
import java.util.concurrent.atomic.AtomicInteger;
public class DiningPhilosophers {
public static void main(String[] args) {
PhilosophersAssistant philosophersAssistant = new PhilosophersAssistant();
Fork f1 = new Fork("1");
Fork f2 = new Fork("2");
Fork f3 = new Fork("3");
Fork f4 = new Fork("4");
Fork f5 = new Fork("5");
new Thread(new Philosophers("第1位哲学家", f1, f5, philosophersAssistant)).start();
new Thread(new Philosophers("第2位哲学家", f2, f1, philosophersAssistant)).start();
new Thread(new Philosophers("第3位哲学家", f3, f2, philosophersAssistant)).start();
new Thread(new Philosophers("第4位哲学家", f4, f3, philosophersAssistant)).start();
new Thread(new Philosophers("第5位哲学家", f5, f4, philosophersAssistant)).start();
}
}
/**
* 助手,帮助哲学家记录当前拿起了左边叉子的人数
*/
class PhilosophersAssistant {
AtomicInteger atomicInteger = new AtomicInteger();
}
class Philosophers extends Thread {
PhilosophersAssistant philosophersAssistant;
private Fork leftFork;
private Fork rightFork;
private String name;
public Philosophers(String name, Fork leftFork, Fork rightFork, PhilosophersAssistant philosophersAssistant) {
this.name = name;
this.leftFork = leftFork;
this.rightFork = rightFork;
this.philosophersAssistant = philosophersAssistant;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
dining();
thinking();
}
}
private void thinking() {
System.out.println(name + "正在思考");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void dining() {
synchronized (leftFork) {
int andIncrement = philosophersAssistant.atomicInteger.getAndIncrement();
//最多只能同时4个科学家拿起左边的叉子
if (andIncrement == 4) {
//主动释放自己当前拿起的左边的叉子
System.out.println(name + "放下了" + leftFork.getName() + "号叉子");
return;
}
System.out.println(name + "拿起了" + leftFork.getName() + "号叉子" + ",等待" + rightFork.getName() + "号叉子");
synchronized (rightFork) {
System.out.println(name + "拿起了" + rightFork.getName() + "号叉子," + "开始用餐");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
philosophersAssistant.atomicInteger.decrementAndGet();
}
}
}
}
class Fork {
private String name;
public Fork(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
2、使用lock方式实现,同样破坏死锁中请求不释放的条件。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class DiningPhilosophers {
public static void main(String[] args) {
ReentrantLock r1 = new ReentrantLock();
ReentrantLock r2 = new ReentrantLock();
ReentrantLock r3 = new ReentrantLock();
ReentrantLock r4 = new ReentrantLock();
ReentrantLock r5 = new ReentrantLock();
Fork f1 = new Fork("1");
Fork f2 = new Fork("2");
Fork f3 = new Fork("3");
Fork f4 = new Fork("4");
Fork f5 = new Fork("5");
new Thread(new Philosophers("第1位哲学家", f1, f5, r1, r5)).start();
new Thread(new Philosophers("第2位哲学家", f2, f1, r2, r1)).start();
new Thread(new Philosophers("第3位哲学家", f3, f2, r3, r2)).start();
new Thread(new Philosophers("第4位哲学家", f4, f3, r4, r3)).start();
new Thread(new Philosophers("第5位哲学家", f5, f4, r5, r4)).start();
}
}
class Philosophers extends Thread {
private Fork leftFork;
private Fork rightFork;
private String name;
private ReentrantLock leftLock;
private ReentrantLock rightLock;
public Philosophers(String name, Fork leftFork, Fork rightFork, ReentrantLock leftLock, ReentrantLock rightLock) {
this.name = name;
this.leftFork = leftFork;
this.rightFork = rightFork;
this.leftLock = leftLock;
this.rightLock = rightLock;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
dining();
thinking();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void thinking() {
System.out.println(name + "正在思考");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void dining() {
try {
boolean tryLock = leftLock.tryLock(1, TimeUnit.SECONDS);
if (!tryLock) {
System.out.println(name + "尝试拿起" + leftFork.getName() + "号叉子失败");
return;
}
System.out.println(name + "拿起了" + leftFork.getName() + "号叉子" + ",等待" + rightFork.getName() + "号叉子");
tryLock = rightLock.tryLock(1, TimeUnit.SECONDS);
if (!tryLock) {
System.out.println(name + "放下了" + leftFork.getName() + "号叉子");
leftLock.unlock();
return;
}
try {
System.out.println(name + "拿起了" + rightFork.getName() + "号叉子," + "开始用餐");
Thread.sleep(2000);
System.out.println(name + "用餐结束,放下了" + leftFork.getName() + "和" + rightFork.getName() + "号叉子");
} finally {
leftLock.unlock();
rightLock.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Fork {
private String name;
public Fork(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
以上4道都是比较常见的笔试题,我刻意分别用了synchronized、lock、Semaphore、阻塞队列,原子类,目的就是为了演示他们各自的实现方式以及区别,多线程编程是非常灵活的,你可以尝试再用这几种方式实现吗?