java-线程等待/同步的五种方法

简介: 在面试时,经常会有面试官问道,一个主线程有多个子线程,如何能使子线程的业务执行完成之后,再执行主线程业务逻辑。对于这个问题,本人能够想到的有五种方法,详细请移步源码 1、使用线程类自带的join方法,将子线程加入到主线程,在子线程执行完之后,在执行主线程逻辑。

在面试时,经常会有面试官问道,一个主线程有多个子线程,如何能使子线程的业务执行完成之后,再执行主线程业务逻辑。对于这个问题,本人能够想到的有五种方法,详细请移步源码

1、使用线程类自带的join方法,将子线程加入到主线程,在子线程执行完之后,在执行主线程逻辑。

例如

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public static void joinDemo()  
  2.         throws InterruptedException  
  3.     {  
  4.         System.out.println("=========Test with join=====");  
  5.         JoinWorker worker1 = new JoinWorker("worker1");  
  6.         JoinWorker worker2 = new JoinWorker("worker2");  
  7.         worker1.start();  
  8.         worker2.start();  
  9.         worker1.join();  
  10.         worker2.join();  
  11.         doSuperWork();  
  12.     }  

2、使用JDK的并发包中的CountDownLatch类, 使用CountDownLatch,每个线程调用其countDown方法使计数器-1,主线程调用await方法阻塞等待,直到CountDownLatch计数器为0时继续执行,例如

首先,定义子线程

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. static class CountDownLatchWorker extends Thread  
  2.     {  
  3.         String workerName;  
  4.   
  5.         CountDownLatch latch;  
  6.   
  7.         public CountDownLatchWorker(String workerName, CountDownLatch latch)  
  8.         {  
  9.             this.workerName = workerName;  
  10.             this.latch = latch;  
  11.         }  
  12.   
  13.         public void run()  
  14.         {  
  15.             System.out.println("Sub Worker " + workerName + " do work begin at "  
  16.                                + sdf.format(new Date()));  
  17.             new ThreadWaitDemo().doSomeWork();// 做实际工作  
  18.             System.out.println("Sub Worker " + workerName + " do work complete at "  
  19.                                + sdf.format(new Date()));  
  20.             latch.countDown();// 完成之后,计数器减一  
  21.   
  22.         }  
  23.     }  

主线程中调研await方法阻塞等待,直到所有线程完成

[html]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public static void countDownLatchDemo()  
  2.         throws InterruptedException  
  3.     {  
  4.         System.out.println("=========Test with CountDownLatch=====");  
  5.         CountDownLatch latch = new CountDownLatch(2);  
  6.         CountDownLatchWorker worker1 = new CountDownLatchWorker("worker1", latch);  
  7.         CountDownLatchWorker worker2 = new CountDownLatchWorker("worker2", latch);  
  8.         worker1.start();  
  9.         worker2.start();  
  10.         //主线程阻塞等待  
  11.         latch.await();  
  12.         doSuperWork();  
  13.     }  

3、使用JDK并发包CyclicBarrier,CyclicBarrier类似于CountDownLatch也是个计数器, 不同的是CyclicBarrier的await()方法没被调用一次,计数便会减少1,并阻塞住当前线程。当计数减至0时,阻塞解除,所有在此 CyclicBarrier 上面阻塞的线程开始运行。 在这之后,如果再次调用 await()方法,计数就又会变成 N-1,新一轮重新开始CyclicBarrier初始时还可带一个Runnable的参数,此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。

示例如下

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public static void cyclicBarrierDemo()  
  2.         throws InterruptedException, BrokenBarrierException  
  3.     {  
  4.         System.out.println("=========Test with CyclicBarrier=====");  
  5.         CyclicBarrier cb = new CyclicBarrier(2new Runnable()  
  6.         {  
  7.             // 将主线程业务放到CyclicBarrier构造方法中,所有线程都到达Barrier时执行  
  8.             @SuppressWarnings("static-access")  
  9.             public void run()  
  10.             {  
  11.                 new ThreadWaitDemo().doSuperWork();  
  12.             }  
  13.         });// 设定需要等待两个线程  
  14.         ExecutorService executor = Executors.newFixedThreadPool(2);  
  15.         CyclicBarrierWorker worker1 = new CyclicBarrierWorker("worker1", cb);  
  16.         CyclicBarrierWorker worker2 = new CyclicBarrierWorker("worker2", cb);  
  17.         executor.execute(worker1);  
  18.         executor.execute(worker2);  
  19.         executor.shutdown();  
  20.     }  

4、使用JDK并发包中的Executors框架,ExecutorService的的invokeAll方法调研callable集合,批量执行多个线程,在invokeAll方法结束之后,再执行主线程其他业务逻辑

示例如下

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. public static void callableDemo()  
  2.         throws InterruptedException  
  3.     {  
  4.         System.out.println("=========Test with Callable=====");  
  5.         List<Callable<Integer>> callList = new ArrayList<Callable<Integer>>();  
  6.         ExecutorService exec = Executors.newFixedThreadPool(2);  
  7.         // 采用匿名内部类实现  
  8.         callList.add(new Callable<Integer>()  
  9.         {  
  10.             public Integer call()  
  11.                 throws Exception  
  12.             {  
  13.                 System.out.println("Sub Worker worker1 do work begin at " + sdf.format(new Date()));  
  14.                 new ThreadWaitDemo().doSomeWork();// 做实际工作  
  15.                 System.out.println("Sub Worker worker1 do work complete at "  
  16.                                    + sdf.format(new Date()));  
  17.                 return 0;  
  18.             }  
  19.         });  
  20.         callList.add(new Callable<Integer>()  
  21.         {  
  22.             public Integer call()  
  23.                 throws Exception  
  24.             {  
  25.                 System.out.println("Sub Worker worker2 do work begin at " + sdf.format(new Date()));  
  26.                 new ThreadWaitDemo().doSomeWork();// 做实际工作  
  27.                 System.out.println("Sub Worker worker2 do work complete at "  
  28.                                    + sdf.format(new Date()));  
  29.                 return 0;  
  30.             }  
  31.         });  
  32.         exec.invokeAll(callList);  
  33.         exec.shutdown();  
  34.         doSuperWork();  
  35.   
  36.     }  
5、这种过于恶心,只简单说一下方法,主线程创建一个线程List,将每个子线程保存到列表中,然后定期轮询列表中子线程状态,当所有线程都完成之后,再执行主线程逻辑
目录
相关文章
|
11天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
13天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
13天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
14天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
37 3
|
14天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
96 2
|
22天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
46 6
|
11天前
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
11天前
|
Java 程序员 调度
【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
创建线程的五种方式,Thread常见方法(守护进程.setDaemon() ,isAlive),start和run方法的区别,如何提前终止一个线程,标志位,isinterrupted,变量捕获
|
14天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
40 1
|
3月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
63 1