失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > java多线程按顺序执行的7种方式

java多线程按顺序执行的7种方式

时间:2023-01-02 08:10:10

相关推荐

java多线程按顺序执行的7种方式

3个线程,按顺序打印A,B,C,一共打印100个字符串

准备:定义使用的枚举有锁操作使用线程通信1:Object 的 wait / notifyAll2:Condition 的 await / signalAll 不使用线程通信1.Semaphore 无锁操作使用线程通信1.LockSupport 的 park / unpark 不使用线程通信1.CyclicBarrier结合AtomicInteger2.AtomicInteger3.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--;}}}}

输出

如果觉得《java多线程按顺序执行的7种方式》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。