Java 如何同步顺序执行多个线程

也许有人会问 “既然用了多线程,为什么还要同步?还要顺序执行呢?”。这个看似脑残的问题其实并非我们想象的那么简单。

 

    假设(这里只是一个假设,类似下面的情形有很多,这里不一一阐述)当你执行定时任务的时候,你需要执行ScheduledExecutorService的一个scheduleAtFixedRate方法的时候,那么你需要给这个方法传入一个线程A的实例。如果这个线程A是一个大的业务,这个大业务里边分多个步骤。假设第一个步骤需要用到多线程,而且业务需求是必须执行完第一步才能执行下面的操作,那么惨了。因为多线程不等第一步运行完毕就有可能执行第二步的操作,那我们该怎么办?

    我们可以把每个步骤看做是一个线程,执行完一个,再执行下一个,那么该如何让这些线程同步并且顺序地执行呢?

    (在网上找了好多资料,有说用join的,但是我没弄出来。于是就自己硬搞了,希望有高手能用join解决一下,谢过!)

 

    第一步是让线程同步,第二部是让线程有顺序。

    同步:我们可以用synchronized来解决。

 

    java线程同步原理: java会为每个object对象分配一个monitor,当某个对象的同步方法(synchronized methods )被多个线程调用时,该对象的monitor将负责处理这些访问的并发独占要求。
当一个线程调用一个对象的同步方法时,JVM会检查该对象的monitor。如果monitor没有被占用,那么这个线程就得到了monitor的占有权,可以继续执行该对象的同步方法;如果monitor被其他线程所占用,那么该线程将被挂起,直到monitor被释放。
当线程退出同步方法调用时,该线程会释放monitor,这将允许其他等待的线程获得monitor以使对同步方法的调用执行下去。就像下面这样:

    public void test() {

        synchronized (this) {
                //做一些事

                //这里只会有一个线程来调用该方法,因为只有一个this对象作为资源分配给该线程
        }

 

    }

 

    顺序:我们可以用List来解决,因为它是有序的。我们只需要将要执行的线程放入到List中不就好解决了吗

 

 

   上代码:

/**
 * 让多个线程同步顺序执行的线程管理器
 * @author bianrx
 * @date 2012.1.18
 * SynchronizedRunMultiThread
 */
public class SyncManager {

    /**
     * 待执行的线程集合,注意这里必须是Runnable接口类型,下面会解释
     */
    private List<Runnable> runnableList;
   
    public SyncManager(){}
   
    public SyncManager(List<Runnable> runnableList) {
        this.runnableList = runnableList;
    }
   
    public void setRunnable(List<Runnable> runnableList) {
        this.runnableList = runnableList;
    }

    public void run() {
        //遍历代执行的线程集合
        for(Runnable runnable: runnableList) {
            runThread(runnable);
        }
    }
   
    /**
     * 按顺序同步执行多个线程
     * @author bianrx
     * @date 2012.1.18
     * @param runnable
     */
    private void runThread(Runnable runnable) {
        synchronized (this) {
            runnable.run();//这里需要注意的是:必须调用run方法,因为如果你调用了start方法,线程只会向JVM请求资源,但是未必就执行其中的run。

            //这个方法是同步的,所以当前只有一个线程占用了this对象。
        }
    }

}

 

 


测试代码:有两个要执行的线程

 

 

public class Thread1 extends Thread {

    @Override
    public void run() {
        System.out.println("运行线程1");
    }

   
}

 

public class Thread2 extends Thread {

    @Override
    public void run() {
        System.out.println("运行线程2");
    }
}

 

//主函数测试

public class MainTest {

    /**
     * @param args
     * @throws ParseException
     * @throws InterruptedException
     */
    public static void main(String[] args) throws ParseException, InterruptedException {
        List<Runnable> runnableList = new ArrayList<Runnable>();
        runnableList.add(new Thread1());
        runnableList.add(new Thread2());
        
        while(true) {
            Thread.sleep(1000);
            new SyncManager(runnableList).run();
            System.out.println("---------------");
        }

}

 

 

 

 

在这里补充一下:我做错了。情况是这样的,我要定时执行一个任务,而我查到的java定时任务的api是这样的:

executorService.scheduleAtFixedRate(srtm, initialDelay, period, unit);

第一个参数是一个runnable接口。这个任务分两个步骤,我为了方便维护程序,就分别写到了两个线程里,实际上算不上并发。但是我的第一步里确实是要有并发的(多线程),我上面写的方法只能保证在主线程里不嵌套子线程的情况下才好用,但是如果有嵌套该怎么办呢?这些是我遇到的情况,大家不必关注。也就是说想做到在主线程中等待多线程都执行完毕了再执行后续代码该怎么办呢?

 

      答案是用join。join方法大家可以查下api,它的意思是等待当前线程执行完后执行完毕才执行其他线程。也就是说如果一个类中有这样一个代码段:

      thread1.start();

      thread2.start();

      thread1.join();

      thread2.join();

     do something 1;

     do something 2;

那么这段代码会等待两个线程执行完毕后再执行 do something 1 和 do something 2,注意:必须先启动所有线程,再join。如果启动一个就join一个,结果是什么?对,那就会是等待thread1执行完再执行thread2,再执行后续代码。

      我就是想告诉大家,真正在做项目时是基本不会遇到红色字体那种情况的。但是经常会遇到让线程同步或者是蓝色代码那种问题。很感谢各位能给出宝贵的点评。

<think>首先,用户的问题是:“我想查找使用Java多个线程顺序执行的方法 请问Java实现多个线程顺序执行的方法”。这似乎是重复的,但核心是询问如何在Java中控制线程顺序执行。 从系统级指令,我需要: - 所有行内数学表达式使用$...$格式(但这个问题与数学无关,所以可能不需要)。 - 独立公式使用$$...$$格式(同样,可能不适用)。 - LaTeX语法正确(由于内容主要是文本和代码,确保代码块格式正确)。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户提供了引用[1]到[5],这些是站内引用,我需要参考它们来回答问题。指令说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。” 用户的上一次问题是相同的查询,所以我没有之前的回答历史,但引用是提供的上下文。 总结引用内容: - [^1]:提到了join()方法、线程优先级、等待/通知链、单例线程池等。 - [^2]:讨论了join()、CountDownLatch、线程池。 - [^3]:强调Java提供多种机制,从join()到Phaser,建议优先高层抽象。 - [^4]:提到了单线程池ExecutorService,以及Semaphore。 - [^5]:列出了多种方式,包括wait/notify、Condition、Semaphore、LockSupport、CyclicBarrier、AtomicInteger等。 用户的需求是找到实现多个线程顺序执行的方法。我需要从引用中提取关键方法,并结构化地回答。 回答结构: - 清晰、逐步解决问题。 - 列出主要方法。 - 提供代码示例(因为引用中有代码)。 - 确保真实可靠。 主要方法从引用中提取: 1. **使用Thread.join()**:让一个线程等待另一个结束。 2. **使用CountDownLatch**:一个线程等待多个线程完成。 3. **使用线程池(如单线程池ExecutorService)**:只有一个线程执行任务,保证顺序。 4. **使用等待/通知机制(synchronized + wait/notify)**:通过Object.wait()和Object.notify()实现顺序。 5. **使用ReentrantLock和Condition**:更灵活的锁机制。 6. **使用Semaphore**:控制访问资源的线程数。 7. **使用Phaser或CyclicBarrier**:高级同步工具。 8. **使用CompletableFuture**(从[^3]提到):高层抽象。 9. **使用LockSupport**(从[^5]提到)。 我应该覆盖最常见的几种方法,并提供简单示例。 回答大纲: 1. **介绍**:简要解释为什么需要控制线程顺序(JVM线程调度不可预测)。 2. **主要方法**:列出并描述每种方法,给出代码示例。 3. **总结**:建议优先使用高层抽象。 4. **生成相关问题**:在末尾添加相关问题。 代码格式:使用代码块,如示例中所示。 语言:中文。 添加引用标识:在引用内容后添加[^1]、[^2]等。 相关问题生成: - 基于主题,生成类似示例的问题,如: 1. 如何选择最合适的线程同步机制? 2. Java线程安全的常见问题? 3. 高级并发工具如CompletableFuture的用法? 确保回答真实可靠:基于标准Java并发知识。 </think>### Java实现多个线程顺序执行的方法 在Java中,JVM的线程调度策略是基于优先级的抢占式调度,同优先级线程执行顺序不可预测[^1]。为了实现多个线程顺序执行,开发者可以利用Java并发包(java.util.concurrent)提供的多种同步机制。以下是几种核心方法,基于引用内容整理,每种方法都附有代码示例和简要说明。优先推荐使用高层抽象(如线程池或CompletableFuture),以确保代码简洁和安全[^3]。 #### 1. **使用Thread.join()方法** 这是最简单的方法,通过让一个线程调用另一个线程的`join()`方法,等待其执行完毕后再启动后续线程。 **原理**:主线程或当前线程阻塞,直到目标线程终止[^1][^2]。 **代码示例**: ```java public class JoinExample { public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(() -> System.out.println("线程1执行")); Thread thread2 = new Thread(() -> System.out.println("线程2执行")); Thread thread3 = new Thread(() -> System.out.println("线程3执行")); thread1.start(); thread1.join(); // 等待thread1结束 thread2.start(); thread2.join(); // 等待thread2结束 thread3.start(); } } ``` **优点**:简单易用,无需额外库。 **缺点**:不适用于大量线程,代码可能冗余[^2]。 #### 2. **使用CountDownLatch** 适用于一个线程等待多个线程完成后继续执行。CountDownLatch通过计数器控制等待[^2]。 **原理**:初始化一个计数器,线程完成时调用`countDown()`,主线程通过`await()`等待计数器归零[^4]。 **代码示例**: ```java import java.util.concurrent.CountDownLatch; public class CountDownLatchExample { public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(3); // 初始化计数器为3 System.out.println("开始执行"); new Thread(() -> { System.out.println("A"); latch.countDown(); }).start(); new Thread(() -> { System.out.println("B"); latch.countDown(); }).start(); new Thread(() -> { System.out.println("C"); latch.countDown(); }).start(); latch.await(); // 等待所有线程完成 System.out.println("所有线程执行完毕"); } } ``` **优点**:灵活支持多线程并发完成后顺序触发。 **缺点**:计数器需预先设置,不适用于动态线程数[^2][^5]。 #### 3. **使用单线程池(ExecutorService)** 通过`Executors.newSingleThreadExecutor()`创建只有一个工作线程线程池,任务按提交顺序执行[^4]。 **原理**:线程池内部维护一个任务队列,确保任务串行执行[^3]。 **代码示例**: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SingleThreadPoolExample { public static void main(String[] args) { ExecutorService executor = Executors.newSingleThreadExecutor(); Runnable task1 = () -> System.out.println("线程1"); Runnable task2 = () -> System.out.println("线程2"); Runnable task3 = () -> System.out.println("线程3"); executor.submit(task1); executor.submit(task2); executor.submit(task3); executor.shutdown(); // 关闭线程池 } } ``` **优点**:高层抽象,自动处理线程生命周期,代码简洁安全。 **缺点**:线程池资源需手动管理,异常可能导致新线程创建[^4][^3]。 #### 4. **使用等待/通知机制(synchronized + wait/notify)** 通过Object的`wait()`和`notify()`方法实现线程间协调,构建顺序链[^1]。 **原理**:每个线程等待前一个线程的通知信号[^5]。 **代码示例**: ```java public class WaitNotifyExample { private static final Object lock = new Object(); private static int current = 1; public static void main(String[] args) { new Thread(() -> print("线程1", 1)).start(); new Thread(() -> print("线程2", 2)).start(); new Thread(() -> print("线程3", 3)).start(); } private static void print(String message, int order) { synchronized (lock) { while (current != order) { // 检查顺序 try { lock.wait(); // 等待通知 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } System.out.println(message); current++; lock.notifyAll(); // 唤醒所有等待线程 } } } ``` **优点**:灵活控制复杂顺序逻辑。 **缺点**:易出错(如死锁),需手动处理同步[^1][^3]。 #### 5. **使用ReentrantLock和Condition** 替代synchronized的更灵活锁机制,Condition提供精准的线程唤醒[^1]。 **原理**:每个线程绑定一个Condition,按顺序唤醒[^3]。 **代码示例**: ```java import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class ConditionExample { private static final ReentrantLock lock = new ReentrantLock(); private static final Condition condition = lock.newCondition(); private static int flag = 1; public static void main(String[] args) { new Thread(() -> runTask("线程1", 1)).start(); new Thread(() -> runTask("线程2", 2)).start(); new Thread(() -> runTask("线程3", 3)).start(); } private static void runTask(String msg, int target) { lock.lock(); try { while (flag != target) { condition.await(); // 等待信号 } System.out.println(msg); flag++; condition.signalAll(); // 唤醒所有 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.unlock(); } } } ``` **优点**:高性能,支持公平锁。 **缺点**:代码复杂度高,需确保锁释放[^3]。 #### 6. **使用Semaphore或Phaser** - **Semaphore**:控制资源访问,通过许可数实现顺序[^4][^5]。 **示例**:初始化Semaphore(1),线程获取许可后执行。 - **Phaser**:高级屏障,适用于分阶段任务[^3]。 **优先推荐**: Semaphore更简单,Phaser适合复杂场景。 #### 总结建议 - **简易场景**:优先使用`Thread.join()`或单线程池(`Executors.newSingleThreadExecutor()`),代码简单且安全[^4]。 - **复杂协调**:选择`CountDownLatch`或`Condition`,支持精细控制[^2][^3]。 - **最佳实践**:避免底层同步(如wait/notify),选择高层工具如`CompletableFuture`(可实现链式顺序),并注意线程安全和资源泄漏问题[^3]。测试执行顺序至关重要,例如使用JUnit并发测试[^3]。 通过以上方法,开发者可以根据任务规模和复杂度选择合适的方案。如需进一步优化,参考Java并发包的官方文档。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值