`
bianrongxin
  • 浏览: 102638 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

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,再执行后续代码。

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

4
3
分享到:
评论
6 楼 DEMONU 2017-04-25  
使用List方式是可以的,不过你这实现不够优雅。可以看Executor这个类
5 楼 rokuan 2012-01-19  
ZaneLee007 写道
aoingl 写道
SyncManager 需要实现 Runnable, 或者继承 Thread 吧。 SyncManager.run() 需要在一个 Runnable.run() 下运行才能与主线程分开。

开启新的线程不是run方法做的,是Thread的native方法start0做的,start0在底层请求JVM分配资源,然后start0会调用你重写的Runnable实例的run方法。Runnable是一个接口,这个接口只有一个方法run,Thread类就实现了这个接口。
所以Java里创建线程都必须要实现这个接口,也就是他们都是Runnable实例。只是Runnable实例并不会启动新的线程,必须调用Thread的start方法,才会启动新的线程。可以这么写:new Thread(Runnable).start()。

嗯,那我的理解应该是对的
4 楼 ZaneLee007 2012-01-19  
aoingl 写道
SyncManager 需要实现 Runnable, 或者继承 Thread 吧。 SyncManager.run() 需要在一个 Runnable.run() 下运行才能与主线程分开。

开启新的线程不是run方法做的,是Thread的native方法start0做的,start0在底层请求JVM分配资源,然后start0会调用你重写的Runnable实例的run方法。Runnable是一个接口,这个接口只有一个方法run,Thread类就实现了这个接口。
所以Java里创建线程都必须要实现这个接口,也就是他们都是Runnable实例。只是Runnable实例并不会启动新的线程,必须调用Thread的start方法,才会启动新的线程。可以这么写:new Thread(Runnable).start()。
3 楼 ZaneLee007 2012-01-19  
rokuan 写道
调用run方法应该没有新建线程,按我的理解,同步就没有意义了,这个产生多线程了吗?求博主答疑啊

run没有启新的线程,start方法才会调用Thread的native的start0方法,start0会调用run方法,开启新的线程,博主这么做,不是多线程顺序执行,而是把业务阻塞在主线程里。请打印:Thread.currentThread().getId(),会发现主线程ID和run里的ID是相同的;如果调用start方法,就不会相同了。
2 楼 aoingl 2012-01-19  
SyncManager 需要实现 Runnable, 或者继承 Thread 吧。 SyncManager.run() 需要在一个 Runnable.run() 下运行才能与主线程分开。
1 楼 rokuan 2012-01-18  
调用run方法应该没有新建线程,按我的理解,同步就没有意义了,这个产生多线程了吗?求博主答疑啊

相关推荐

    java10个线程按照顺序打印1-100

    然而,当多个线程需要共享资源或按照特定顺序执行时,就需要进行线程同步。 1. **synchronized**:在Java中,`synchronized`关键字用于提供互斥访问,确保同一时间只有一个线程可以访问特定的代码块。例如,可以...

    JAVA线程高级-线程按序交替执行

    在Java编程中,多线程是并发编程的重要组成部分,它允许程序同时执行多个任务,从而提高了系统的效率和响应性。然而,在某些场景下,我们可能需要控制线程的执行顺序,确保它们按照特定的顺序交替运行,这在并发编程...

    java多线程Demo

    在多线程环境下,如果一个变量被多个线程共享且只进行读写操作,可以使用volatile保证数据的一致性。 9. sleep(), yield(), interrupt(): - sleep()方法使当前线程暂停指定的时间,然后继续执行。 - yield()方法...

    java 线程同步 信号量控制同步

    线程同步是 Java 编程中的一种机制,用于控制多个线程之间的资源访问顺序,以避免线程之间的冲突和数据不一致。线程同步的目的就是避免线程“同步”执行,即让多个线程之间排队操作共享资源。 关于线程同步,需要...

    多线程并行执行,汇总结果

    多线程并行执行是指在同一个程序中同时运行多个线程,每个线程负责不同的任务,以实现任务的并发执行。这种技术可以充分利用多核处理器的优势,将CPU的计算能力最大化,从而提高程序的运行效率。在Java中,可以通过...

    java 两个线程相互顺序输出

    - 在Java中,线程同步是为了避免多个线程对共享资源的不正确访问,防止数据的不一致性。Java提供了多种同步机制,如`synchronized`关键字、`Lock`接口(如`ReentrantLock`)、`Semaphore`信号量、`CyclicBarrier`...

    java多线程每个线程挨着打印ABC的4种实现方式

    java多线程每个线程挨着打印ABC的4种实现方式,有4个线程t1、t2、t3、t4,t1打印A后t2打印A再t3打印A再t4打印A,然后从新回到t1打印B再t2打印B...t4打印B... 4个线程轮流打印abc... 一个线程可以理解为一个人,打印...

    java多线程设计模式_java_设计模式_多线程_多线程课题_

    死锁是多线程编程中的常见问题,两个或多个线程互相等待对方释放资源,导致无法继续执行。Java的jstack工具可以帮助检测死锁,而在编程时应避免循环等待,合理设计锁的获取顺序,以减少死锁的风险。 以上就是Java多...

    java多线程的讲解和实战

    - 同步机制:为了防止多个线程访问同一资源时产生数据不一致的问题,Java提供了`synchronized`关键字,可以锁定代码块或整个方法,确保同一时刻只有一个线程执行。 - 等待/通知机制:`wait()`, `notify()`, `...

    Java多线程按指定顺序同步执行

    Java多线程按指定顺序同步执行是Java编程中的一种常见问题,即如何让多个线程按照既定的顺序依次执行。下面将介绍三种解决方案,分别使用newSingleThreadExecutor、join方法和ThreadPoolExecutor。 方法1:使用...

    java 多线程示例

    Java提供了synchronized关键字来实现线程同步,确保同一时刻只有一个线程能执行特定代码块。 5. **死锁** 死锁是两个或多个线程相互等待对方释放资源而造成的僵局。避免死锁的关键在于遵循资源请求的顺序化和避免...

    java 多线程同步

    Java多线程同步是Java编程中关键的并发概念,它涉及到如何在多个线程访问共享资源时保持数据的一致性和完整性。`java.util.concurrent`包是Java提供的一个强大的并发工具库,它为开发者提供了多种线程安全的工具,...

    java多线程代码案例(创建线程,主线程,线程优先级,线程组,线程同步,线程间的通信)

    线程同步是为了防止多个线程同时访问共享资源导致的数据不一致问题。Java提供了多种同步机制,包括`synchronized`关键字、`wait()`, `notify()`, `notifyAll()`方法、`Lock`接口(如`ReentrantLock`)以及`...

    java线程同步及通信

    `Synch.java`的`main`方法中,通过`join()`方法确保`ob1`、`ob2`和`ob3`线程按照创建的顺序执行,这也是线程同步的一种体现。 2. **线程间通信**: 在多线程环境中,线程之间可能需要交换数据或协调工作。Java提供...

    java多线程文件传输

    - **CyclicBarrier**:允许多个线程等待直到达到某个屏障点再一起继续执行。 - **Semaphore**:信号量,控制同时访问特定资源的线程数量。 - **ThreadPoolExecutor**:线程池,管理线程并处理任务队列,能有效...

    java线程线程安全同步线程

    同步线程则是指通过特定机制(如锁)来控制多个线程访问共享资源的顺序,避免出现竞态条件和死锁。 在Java中,线程的创建可以通过继承`Thread`类或实现`Runnable`接口。线程的主体行为定义在`run()`方法中,当`...

    java多线程示例

    2. CyclicBarrier:允许多个线程等待至某个屏障点,然后一起继续执行。 3. Semaphore:信号量,用于限制同时访问某个资源的线程数量。 4. Executors框架:提供线程池服务,有助于管理和控制线程的生命周期。 六、...

    多个线程到达后才能执行某个任务,并且只能执行一次

    标题中的“多个线程到达后才能执行某个任务,并且只能执行一次”指的是在多线程环境下,需要确保一个任务在所有等待线程都到达特定条件后才开始执行,并且这个任务在整个程序运行过程中只被执行一次。这通常涉及到...

    java 多线程课件

    实现Runnable接口时,可以避免单继承的限制,同时允许多个线程共享同一资源。 Java中的线程调度包括抢占式调度和优先级调度。抢占式调度意味着高优先级的线程可以中断正在执行的低优先级线程。Java提供了...

Global site tag (gtag.js) - Google Analytics