java多线程按顺序执行的7种方式
3个线程,按顺序打印A,B,C,一共打印100个字符串
- 准备:定义使用的枚举
- 有锁操作
- 使用线程通信
- 1:Object 的 wait / notifyAll
- 2:Condition 的 await / signalAll
- 不使用线程通信
- 1.Semaphore
- 无锁操作
- 使用线程通信
- 1.LockSupport 的 park / unpark
- 不使用线程通信
- 1.CyclicBarrier结合AtomicInteger
- 2.AtomicInteger
- 3.CountDownLatch
准备:定义使用的枚举
/*** 符号枚举** @author Administrator*/
public enum SignEnum {/**/A {@Overridepublic SignEnum last() {return C;}},B {@Overridepublic SignEnum last() {return A;}},C {@Overridepublic SignEnum last() {return B;}};/*** 上一个符号** @return 符号*/public abstract SignEnum last();
}
有锁操作
使用线程通信
1:Object 的 wait / notifyAll
/*** 通过wait/notify打印** @author Administrator*/
public class WaitNotifyTest {/*** 锁*/private final Object lock = new Object();/*** 当前符号*/private volatile SignEnum currentSign = SignEnum.C;/*** 自定义线程池*/private final ThreadPoolExecutor executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors() * 2, 5, TimeUnit.MINUTES,new LinkedBlockingQueue<>(3), Thread::new, new ThreadPoolExecutor.AbortPolicy());/*** 打印*/@Test@SneakyThrowspublic void testPrint() {// 打印BFuture<?> bFuture = executor.submit(() -> doPrint(33, SignEnum.B));// 打印AFuture<?> aFuture = executor.submit(() -> doPrint(34, SignEnum.A));// 打印CFuture<?> cFuture = executor.submit(() -> doPrint(33, SignEnum.C));// 等待打印完成while (!aFuture.isDone() || !bFuture.isDone() || !cFuture.isDone()) {TimeUnit.MILLISECONDS.sleep(5);}// 关闭线程池executor.shutdown();}/*** 执行打印** @param count 次数* @param targetSign 目标符号*/@SneakyThrowsprivate void doPrint(int count, SignEnum targetSign) {synchronized (lock) {for (int index = 0; index < count; index++) {// 如果当前符号为目标符号的上一个符号,执行打印符号if (Objects.equals(targetSign.last(), currentSign)) {// 打印目标符号System.out.print(targetSign.name());// 打印分割符,方便观察if (Objects.equals(SignEnum.C, targetSign)) {System.out.print("|");}// 切换当前符号currentSign = targetSign;// 释放锁,通知其他线程// 不能使用notify,如果通知的某个线程不符合条件,进行等待,将会造成程序阻塞lock.notifyAll();}// 不匹配的话,index回退,同时进行等待else {index--;lock.wait();}}}}
}
输出

2:Condition 的 await / signalAll
/*** 通过condition打印** @author Administrator*/
public class ConditionTest {/*** 锁*/private final Lock lock = new ReentrantLock();/*** 条件*/private final Condition condition = lock.newCondition();/*** 当前符号*/private volatile SignEnum currentSign = SignEnum.C;/*** 自定义线程池*/private final ThreadPoolExecutor executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors() * 2, 5, TimeUnit.MINUTES,new LinkedBlockingQueue<>(3), Thread::new, new ThreadPoolExecutor.AbortPolicy());/*** 打印*/@Test@SneakyThrowspublic void testPrint() {// 打印BFuture<?> bFuture = executor.submit(() -> doPrint(33, SignEnum.B));// 打印AFuture<?> aFuture = executor.submit(() -> doPrint(34, SignEnum.A));// 打印CFuture<?> cFuture = executor.submit(() -> doPrint(33, SignEnum.C));// 等待打印完成while (!aFuture.isDone() || !bFuture.isDone() || !cFuture.isDone()) {TimeUnit.MILLISECONDS.sleep(5);}// 关闭线程池executor.shutdown();}/*** 执行打印** @param count 次数* @param targetSign 目标符号*/private void doPrint(int count, SignEnum targetSign) {lock.lock();try {for (int index = 0; index < count; index++) {// 如果当前符号为目标符号的上一个符号,执行打印符号if (Objects.equals(targetSign.last(), currentSign)) {// 打印目标符号System.out.print(targetSign.name());// 打印分割符,方便观察if (Objects.equals(SignEnum.C, targetSign)) {System.out.print("|");}// 切换当前符号currentSign = targetSign;// 释放锁,通知其他线程// 不能使用signal,如果通知的某个线程不符合条件,进行等待,将会造成程序阻塞condition.signalAll();}// 不匹配的话,index回退,同时进行等待else {index--;condition.await();}}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}
输出

不使用线程通信
1.Semaphore
/*** 通过Semaphore打印** @author Administrator*/
public class SemaphoreTest {/*** 当前符号*/private volatile SignEnum currentSign = SignEnum.C;/*** 信号量*/private final Semaphore semaphore = new Semaphore(1);/*** 自定义线程池*/private final ThreadPoolExecutor executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors() * 2, 5, TimeUnit.MINUTES,new LinkedBlockingQueue<>(3), Thread::new, new ThreadPoolExecutor.AbortPolicy());/*** 打印*/@Test@SneakyThrowspublic void testPrint() {// 打印BFuture<?> bFuture = executor.submit(() -> doPrint(33, SignEnum.B));// 打印AFuture<?> aFuture = executor.submit(() -> doPrint(34, SignEnum.A));// 打印CFuture<?> cFuture = executor.submit(() -> doPrint(33, SignEnum.C));// 等待打印完成while (!aFuture.isDone() || !bFuture.isDone() || !cFuture.isDone()) {TimeUnit.MILLISECONDS.sleep(5);}// 关闭线程池executor.shutdown();}/*** 执行打印** @param count 次数* @param targetSign 目标符号*/@SneakyThrowsprivate void doPrint(int count, SignEnum targetSign) {for (int index = 0; index < count; index++) {// 尝试获取令牌,与锁的效果类似if (semaphore.tryAcquire()) {try {// 如果当前符号为目标符号的上一个符号,执行打印符号if (Objects.equals(targetSign.last(), currentSign)) {// 打印目标符号System.out.print(targetSign.name());// 打印分割符,方便观察if (Objects.equals(SignEnum.C, targetSign)) {System.out.print("|");}// 切换当前符号+currentSign = targetSign;}// 不匹配的话,index回退else {index--;}} catch (Exception e) {e.printStackTrace();} finally {// 释放令牌semaphore.release();}}// 获取不到令牌,index回退else {index--;}}}
}
输出

无锁操作
使用线程通信
1.LockSupport 的 park / unpark
/*** 通过LockSupport打印** @author Administrator*/
public class LockSupportTest {/*** 当前符号*/private volatile SignEnum currentSign = SignEnum.C;/*** 线程集合*/private final List<Thread> threadList = new ArrayList<>(3);/*** 自定义线程池*/private final ThreadPoolExecutor executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors() * 2, 5, TimeUnit.MINUTES,new LinkedBlockingQueue<>(3), r -> {Thread thread = new Thread(r);threadList.add(thread);return thread;}, new ThreadPoolExecutor.AbortPolicy());/*** 打印*/@Test@SneakyThrowspublic void testPrint() {// 打印BFuture<?> bFuture = executor.submit(() -> doPrint(33, SignEnum.B));// 打印AFuture<?> aFuture = executor.submit(() -> doPrint(34, SignEnum.A));// 打印CFuture<?> cFuture = executor.submit(() -> doPrint(33, SignEnum.C));// 等待打印完成while (!aFuture.isDone() || !bFuture.isDone() || !cFuture.isDone()) {TimeUnit.MILLISECONDS.sleep(5);}// 关闭线程池executor.shutdown();}/*** 执行打印** @param count 次数* @param targetSign 目标符号*/@SneakyThrowsprivate void doPrint(int count, SignEnum targetSign) {// 不能使用锁,unpark方法不会将锁释放,造成程序阻塞for (int index = 0; index < count; index++) {// 如果当前符号为目标符号的上一个符号,执行打印符号if (Objects.equals(targetSign.last(), currentSign)) {// 打印目标符号System.out.print(targetSign.name());// 打印分割符,方便观察if (Objects.equals(SignEnum.C, targetSign)) {System.out.print("|");}// 切换当前符号currentSign = targetSign;// 通知其他线程Thread currentThread = Thread.currentThread();threadList.forEach(thread -> {if (!Objects.equals(currentThread, thread)) {LockSupport.unpark(thread);}});}// 不匹配的话,index回退,同时进行等待else {index--;LockSupport.park();}}}
}
输出

不使用线程通信
1.CyclicBarrier结合AtomicInteger
/*** 通过CyclicBarrier打印** @author Administrator*/
public class CyclicBarrierTest {/*** 当前符号*/private volatile SignEnum currentSign = SignEnum.C;/*** 计数器*/private final AtomicInteger atomicInteger = new AtomicInteger();/*** 篱栅*/private final CyclicBarrier cyclicBarrier = new CyclicBarrier(3, atomicInteger::incrementAndGet);/*** 自定义线程池*/private final ThreadPoolExecutor executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors() * 2, 5, TimeUnit.MINUTES,new LinkedBlockingQueue<>(3), Thread::new, new ThreadPoolExecutor.AbortPolicy());/*** 打印*/@Test@SneakyThrowspublic void testPrint() {// 打印Bexecutor.execute(() -> doPrint(33, SignEnum.B));// 打印Aexecutor.execute(() -> doPrint(34, SignEnum.A));// 打印Cexecutor.execute(() -> doPrint(33, SignEnum.C));// 等待打印完成while (atomicInteger.get() != 34) {TimeUnit.MILLISECONDS.sleep(5);}// 关闭线程池executor.shutdown();}/*** 执行打印** @param count 次数* @param targetSign 目标符号*/@SneakyThrowsprivate void doPrint(int count, SignEnum targetSign) {for (int index = 0; index < count; index++) {// 如果当前符号为目标符号的上一个符号,执行打印符号if (Objects.equals(targetSign.last(), currentSign)) {// 打印目标符号System.out.print(targetSign.name());// 打印分割符,方便观察if (Objects.equals(SignEnum.C, targetSign)) {System.out.print("|");}// 切换当前符号currentSign = targetSign;// 阻塞等待cyclicBarrier.await();}// 不匹配的话,index回退else {index--;}}// B,C线程进行阻塞,保证cyclicBarrier走完,否则程序会阻塞if (count == 33) {cyclicBarrier.await();}}
}
输出

2.AtomicInteger
/*** 通过AtomicInteger打印** @author Administrator*/
public class AtomicIntegerTest {/*** 当前符号*/private volatile SignEnum currentSign = SignEnum.C;/*** 计数器*/private final AtomicInteger counter = new AtomicInteger();/*** 自定义线程池*/private final ThreadPoolExecutor executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors() * 2, 5, TimeUnit.MINUTES,new LinkedBlockingQueue<>(3), Thread::new, new ThreadPoolExecutor.AbortPolicy());/*** 打印*/@Test@SneakyThrowspublic void testPrint() {// 打印Bexecutor.execute(() -> doPrint(33, SignEnum.B));// 打印Aexecutor.execute(() -> doPrint(34, SignEnum.A));// 打印Cexecutor.execute(() -> doPrint(33, SignEnum.C));// 等待打印完成while (counter.get() != 100) {TimeUnit.MILLISECONDS.sleep(5);}// 关闭线程池executor.shutdown();}/*** 执行打印** @param count 次数* @param targetSign 目标符号*/private void doPrint(int count, SignEnum targetSign) {for (int index = 0; index < count; index++) {// 如果当前符号为目标符号的上一个符号,执行打印符号if (Objects.equals(targetSign.last(), currentSign)) {// 打印目标符号System.out.print(targetSign.name());// 打印分割符,方便观察if (Objects.equals(SignEnum.C, targetSign)) {System.out.print("|");}// 切换当前符号currentSign = targetSign;// 计数+1counter.incrementAndGet();}// 不匹配的话,index回退else {index--;}}}
}
输出

3.CountDownLatch
/*** 通过countDownLatch打印** @author Administrator*/
public class CountDownLatchTest {/*** 当前符号*/private volatile SignEnum currentSign = SignEnum.C;/*** 计数器*/private final CountDownLatch countDownLatch = new CountDownLatch(100);/*** 自定义线程池*/private final ThreadPoolExecutor executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors() * 2, 5, TimeUnit.MINUTES,new LinkedBlockingQueue<>(3), Thread::new, new ThreadPoolExecutor.AbortPolicy());/*** 打印*/@Test@SneakyThrowspublic void testPrint() {// 打印Bexecutor.execute(() -> doPrint(33, SignEnum.B));// 打印Aexecutor.execute(() -> doPrint(34, SignEnum.A));// 打印Cexecutor.execute(() -> doPrint(33, SignEnum.C));// 等待打印完成countDownLatch.await();// 关闭线程池executor.shutdown();}/*** 执行打印** @param count 次数* @param targetSign 目标符号*/@SneakyThrowsprivate void doPrint(int count, SignEnum targetSign) {for (int index = 0; index < count; index++) {// 如果当前符号为目标符号的上一个符号,执行打印符号if (Objects.equals(targetSign.last(), currentSign)) {try {// 打印目标符号System.out.print(targetSign.name());// 打印分割符,方便观察if (Objects.equals(SignEnum.C, targetSign)) {System.out.print("|");}// 切换当前符号+currentSign = targetSign;} catch (Exception e) {e.printStackTrace();} finally {// 计数减1countDownLatch.countDown();}}// 不匹配的话,index回退else {index--;}}}
}
输出

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
