本文共 16587 字,大约阅读时间需要 55 分钟。
同时启动三个线程,分别为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(); } }}
同时启动两个线程,分别为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(); ListgoodsList = 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(); LinkedBlockingDequegoodsList = 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() + "件商品"); }}
第一位哲学家和第二位哲学家 共享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、阻塞队列,原子类,目的就是为了演示他们各自的实现方式以及区别,多线程编程是非常灵活的,你可以尝试再用这几种方式实现吗?
转载地址:http://golrb.baihongyu.com/