1、用5个线程去卖火车票,一共5000张,卖完即止。
用线程池的写法:向线程池提交5000个任务
public class Test { public static void main(String[] args) throws InterruptedException { int total = 5000; int threadSize = 5; TicketHolder ticketHolder = new TicketHolder(total); ExecutorService executorService = Executors.newFixedThreadPool(threadSize, new ThreadFactoryBuilder().setNameFormat("线程%d").build()); for (int i = 1; i <= total; i++) { executorService.submit(ticketHolder::decrease); } executorService.awaitTermination(3, TimeUnit.SECONDS); executorService.shutdown(); } } @Data @AllArgsConstructor class TicketHolder { private int total; public synchronized void decrease() { total = total - 1; System.out.println(Thread.currentThread().getName() + ",还剩" + total + "张票"); } }
以上代码可以优化下,去掉实体类,而是用静态变量在多个线程间传值:
用线程池的写法:向线程池提交5个任务
public class Test { public static void main(String[] args) throws InterruptedException { int total = 5000; int threadSize = 5; TicketHolder ticketHolder = new TicketHolder(total); ExecutorService executorService = Executors.newFixedThreadPool(threadSize, new ThreadFactoryBuilder().setNameFormat("线程%d").build()); Runnable runnable = new Runnable() { @Override public void run() { System.out.println(1); while (true) { synchronized (this) { if (ticketHolder.getTotal() >= 1) { ticketHolder.decrease(); } else { break; } } } } }; for (int i = 1; i <= threadSize; i++) { executorService.submit(runnable); } executorService.awaitTermination(3, TimeUnit.SECONDS); executorService.shutdown(); } } @Data @AllArgsConstructor class TicketHolder { private int total; public void decrease() { total = total - 1; System.out.println(Thread.currentThread().getName() + ",还剩" + total + "张票"); } }
不用线程池的写法:
public class Test { public static void main(String[] args) { int total = 5000; TicketHolder ticketHolder = new TicketHolder(total); new Thread(new SellTicketThread(ticketHolder), "线程1").start(); new Thread(new SellTicketThread(ticketHolder), "线程2").start(); new Thread(new SellTicketThread(ticketHolder), "线程3").start(); new Thread(new SellTicketThread(ticketHolder), "线程4").start(); new Thread(new SellTicketThread(ticketHolder), "线程5").start(); } } @Data @AllArgsConstructor class TicketHolder { private int total; public void decrease() { if (total >= 1) { total = total - 1; System.out.println(Thread.currentThread() + ",还剩" + total + "张票"); } } } class SellTicketThread implements Runnable { private final TicketHolder ticketHolder; @Override public void run() { while (ticketHolder.getTotal() >= 1) { synchronized (ticketHolder) { ticketHolder.decrease(); } } } public SellTicketThread(TicketHolder ticketHolder) { this.ticketHolder = ticketHolder; } }
2、三个线程,依次打印A、B、C,打印十次,各线程run方法只能执行一次,最后依次输出ABCABCABC...。
用线程池写法:因为各线程run()方法只能执行一次,所以只能往线程池中提交三个任务。如果提交三十个任务的话,run()方法会执行三十次。
public class Test { public static void main(String[] args) throws InterruptedException { PrintTime printTime = new PrintTime(0); ExecutorService executorService = Executors.newFixedThreadPool(3, new ThreadFactoryBuilder().setNameFormat("线程%d").build()); Runnable runnableA = () -> { for (int i = 1; i <= 10; i++) { synchronized (printTime) { if (printTime.getTime() % 3 == 0) { System.out.print("A"); printTime.setTime(printTime.getTime() + 1); } else { i = i - 1; } } } }; Runnable runnableB = () -> { for (int i = 1; i <= 10; i++) { synchronized (printTime) { if (printTime.getTime() % 3 == 1) { System.out.print("B"); printTime.setTime(printTime.getTime() + 1); } else { i = i - 1; } } } }; Runnable runnableC = () -> { for (int i = 1; i <= 10; i++) { synchronized (printTime) { if (printTime.getTime() % 3 == 2) { System.out.print("C"); printTime.setTime(printTime.getTime() + 1); } else { i = i - 1; } } } }; executorService.submit(runnableA); executorService.submit(runnableB); executorService.submit(runnableC); executorService.awaitTermination(3, TimeUnit.SECONDS); executorService.shutdown(); } } @Data @AllArgsConstructor class PrintTime { int time; }
不用线程池写法:
public class Test { public static void main(String[] args) { PrintTime object = new PrintTime(0); new Thread(new PrintThread(object, "A"), "线程1").start(); new Thread(new PrintThread(object, "B"), "线程2").start(); new Thread(new PrintThread(object, "C"), "线程3").start(); } } @Data @AllArgsConstructor class PrintTime { int time; } class PrintThread implements Runnable { private final PrintTime printTime; private String printStr; public PrintThread(PrintTime printTime, String printStr) { this.printTime = printTime; this.printStr = printStr; } @Override public void run() { while (true) { synchronized (printTime) { if (printTime.getTime() >= 30) { break; } if (("A".equals(printStr) && printTime.getTime() % 3 == 0) || "B".equals(printStr) && printTime.getTime() % 3 == 1 || "C".equals(printStr) && printTime.getTime() % 3 == 2 ) { System.out.print(printStr); printTime.setTime(printTime.getTime() + 1); } } } } }
不用synchronized,用Lock写法:
public class Test { public static void main(String[] args) { Lock lock = new ReentrantLock(); PrintTime printTime = new PrintTime(0); new Thread(new ThreadA(lock, printTime), "线程A").start(); new Thread(new ThreadB(lock, printTime), "线程B").start(); new Thread(new ThreadC(lock, printTime), "线程C").start(); } } class ThreadA implements Runnable { private Lock lock; private PrintTime printTime; public ThreadA(Lock lock, PrintTime printTime) { this.lock = lock; this.printTime = printTime; } @Override public void run() { int size = 0; while (size < 10) { if (lock.tryLock()) { try { if (printTime.getTime() % 3 == 0) { size = size + 1; printTime.setTime(printTime.getTime() + 1); System.out.println("A"); } } finally { lock.unlock(); } } } } } class ThreadB implements Runnable { private Lock lock; private PrintTime printTime; public ThreadB(Lock lock, PrintTime printTime) { this.lock = lock; this.printTime = printTime; } @Override public void run() { int size = 0; while (size < 10) { if (lock.tryLock()) { try { if (printTime.getTime() % 3 == 1) { size = size + 1; printTime.setTime(printTime.getTime() + 1); System.out.println("B"); } } finally { lock.unlock(); } } } } } class ThreadC implements Runnable { private Lock lock; private PrintTime printTime; public ThreadC(Lock lock, PrintTime printTime) { this.lock = lock; this.printTime = printTime; } @Override public void run() { int size = 0; while (size < 10) { if (lock.tryLock()) { try { if (printTime.getTime() % 3 == 2) { size = size + 1; printTime.setTime(printTime.getTime() + 1); System.out.println("C"); } } finally { lock.unlock(); } } } } } @Data @AllArgsConstructor class PrintTime { int time; }
注意,unlock()方法一定要写在finally块中。
用wait/notify机制:
用Condition的await/signal通信机制:
A线程打印完,唤醒B,且本身await,B线程打印完,唤醒C,且本身await,C线程打印完,唤醒A,且本身await。。。依次
public class AwaitSignalTest { public static void main(String[] args) { Lock lock = new ReentrantLock(); Condition conditionA = lock.newCondition(); Condition conditionB = lock.newCondition(); Condition conditionC = lock.newCondition(); int threadSize = 3; int printTimePerThread = 10; int maxPrintTime = threadSize * printTimePerThread; new Thread(new Print(lock, conditionA, conditionB, "A", threadSize, maxPrintTime, 0)).start(); new Thread(new Print(lock, conditionB, conditionC, "B", threadSize, maxPrintTime, 1)).start(); new Thread(new Print(lock, conditionC, conditionA, "C", threadSize, maxPrintTime, 2)).start(); } } class Print implements Runnable { private static int printTimeInt = 0; private Lock lock; private final Condition conditionToWait; private final Condition conditionNotify; private String printStr; private int threadSize; private int maxPrintTime; private int index; public Print(Lock lock, Condition conditionToWait, Condition conditionNotify, String printStr, int threadSize, int maxPrintTime, int index) { this.lock = lock; this.conditionToWait = conditionToWait; this.conditionNotify = conditionNotify; this.printStr = printStr; this.threadSize = threadSize; this.maxPrintTime = maxPrintTime; this.index = index; } @Override public void run() { while (true) { lock.lock(); if (printTimeInt >= maxPrintTime) { System.exit(1); } if (printTimeInt % threadSize == index) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(printStr); printTimeInt++; conditionNotify.signal(); } try { conditionToWait.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
上面的代码不是一次写好的,是先把三个线程的实现分别写出来,调通,然后一步步调优得到的。代码中的System.exit(1);是让jvm退出,自己习惯是lock.unlock();break;,但是这样不行,原因还没定位出来。
一个Lock实例可以生成多个Condition实例,而调用任意一个Condition实例的await()方法都会使调用线程释放掉这个Lock实例,如果在await之前先调用另一个Condition实例的signal()方法,则相当于先唤醒另一个线程,然后让本线程等待唤醒。这样的特性非常适合于多线程中各线程按照自定义顺序执行的场景,但是编程难度也是很大的,要debug好久。