Java中怎么保证线程顺序执行
小编给大家分享一下Java中怎么保证线程顺序执行,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
只要了解过多线程,我们就知道线程开始的顺序跟执行的顺序是不一样的。如果只是创建三个线程然后执行,最后的执行顺序是不可预期的。这是因为在创建完线程之后,线程执行的开始时间取决于CPU何时分配时间片,线程可以看成是相对于的主线程的一个异步操作。
publicclassFIFOThreadExample{publicsynchronizedstaticvoidfoo(Stringname){System.out.print(name);}publicstaticvoidmain(String[]args){Threadthread1=newThread(()->foo("A"));Threadthread2=newThread(()->foo("B"));Threadthread3=newThread(()->foo("C"));thread1.start();thread2.start();thread3.start();}}
输出结果:ACB/ABC/CBA...
那么我们该如何保证线程的顺序执行呢?
如何保证线程的顺序执行?1. 使用Thread.join()实现Thread.join()
的作用是让父线程等待子线程结束之后才能继续运行。以上述例子为例,main()
方法所在的线程是父线程,在其中我们创建了3个子线程A,B,C,子线程的执行相对父线程是异步的,不能保证顺序性。而对子线程使用Thread.join()
方法之后就可以让父线程等待子线程运行结束后,再开始执行父线程,这样子线程执行被强行变成了同步的,我们用Thread.join()
方法就能保证线程执行的顺序性。
publicclassFIFOThreadExample{publicstaticvoidfoo(Stringname){System.out.print(name);}publicstaticvoidmain(String[]args)throwsInterruptedException{Threadthread1=newThread(()->foo("A"));Threadthread2=newThread(()->foo("B"));Threadthread3=newThread(()->foo("C"));thread1.start();thread1.join();thread2.start();thread2.join();thread3.start();}}
2. 使用单线程线程池来实现输出结果:ABC
另一种保证线程顺序执行的方法是使用一个单线程的线程池,这种线程池中只有一个线程,相应的,内部的线程会按加入的顺序来执行。
importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;publicclassFIFOThreadExample{publicstaticvoidfoo(Stringname){System.out.print(name);}publicstaticvoidmain(String[]args)throwsInterruptedException{Threadthread1=newThread(()->foo("A"));Threadthread2=newThread(()->foo("B"));Threadthread3=newThread(()->foo("C"));ExecutorServiceexecutor=Executors.newSingleThreadExecutor();executor.submit(thread1);executor.submit(thread2);executor.submit(thread3);executor.shutdown();}}
3. 使用volatile关键字修饰的信号量实现输出结果:ABC
上面两种的思路都是让保证线程的执行顺序,让线程按一定的顺序执行。这里介绍第三种思路,那就是线程可以无序运行,但是执行结果按顺序执行。
你应该可以想到,三个线程都被创建并start()
,这时候三个线程随时都可能执行run()
方法。因此为了保证run()
执行的顺序性,我们肯定需要一个信号量来让线程知道在任意时刻能不能执行逻辑代码。
另外,因为三个线程是独立的,这个信号量的变化肯定需要对其他线程透明,因此volatile关键字也是必须要的。
publicclassTicketExample2{//信号量staticvolatileintticket=1;//线程休眠时间publicfinalstaticintSLEEP_TIME=1;publicstaticvoidfoo(intname){//因为线程的执行顺序是不可预期的,因此需要每个线程自旋while(true){if(ticket==name){try{Thread.sleep(SLEEP_TIME);//每个线程循环打印3次for(inti=0;i<3;i++){System.out.println(name+""+i);}}catch(InterruptedExceptione){e.printStackTrace();}//信号量变更ticket=name%3+1;return;}}}publicstaticvoidmain(String[]args)throwsInterruptedException{Threadthread1=newThread(()->foo(1));Threadthread2=newThread(()->foo(2));Threadthread3=newThread(()->foo(3));thread1.start();thread2.start();thread3.start();}}
4. 使用Lock和信号量实现执行结果:
1 0
1 1
1 2
2 0
2 1
2 2
3 0
3 1
3 2
此种方法的思想跟第三种方法是一样的,都是不考虑线程执行的顺序而是考虑用一些方法控制线程执行业务逻辑的顺序。这里我们同样用一个原子类型信号量ticket
,当然你可以不用原子类型,这里我只是为了保证自增操作的线程安全。然后我们用了一个可重入锁ReentrantLock
。用来给方法加锁,当一个线程拿到锁并且标识位正确的时候开始执行业务逻辑,执行完毕后唤醒下一个线程。
这里我们不需要使用while进行自旋操作了,因为Lock可以让我们唤醒指定的线程,所以改成if就可以实现顺序的执行。
publicclassTicketExample3{//信号量AtomicIntegerticket=newAtomicInteger(1);publicLocklock=newReentrantLock();privateConditioncondition1=lock.newCondition();privateConditioncondition2=lock.newCondition();privateConditioncondition3=lock.newCondition();privateCondition[]conditions={condition1,condition2,condition3};publicvoidfoo(intname){try{lock.lock();//因为线程的执行顺序是不可预期的,因此需要每个线程自旋System.out.println("线程"+name+"开始执行");if(ticket.get()!=name){try{System.out.println("当前标识位为"+ticket.get()+",线程"+name+"开始等待");//开始等待被唤醒conditions[name-1].await();System.out.println("线程"+name+"被唤醒");}catch(InterruptedExceptione){e.printStackTrace();}}System.out.println(name);ticket.getAndIncrement();if(ticket.get()>3){ticket.set(1);}//执行完毕,唤醒下一次。1唤醒2,2唤醒3conditions[name%3].signal();}finally{//一定要释放锁lock.unlock();}}publicstaticvoidmain(String[]args)throwsInterruptedException{TicketExample3example=newTicketExample3();Threadt1=newThread(()->{example.foo(1);});Threadt2=newThread(()->{example.foo(2);});Threadt3=newThread(()->{example.foo(3);});t1.start();t2.start();t3.start();}}
输出结果:
线程2 开始执行
当前标识位为1,线程2 开始等待
线程1 开始执行
1
线程3 开始执行
当前标识位为2,线程3 开始等待
线程2 被唤醒
2
线程3 被唤醒
3
上述的执行结果并非唯一,但可以保证打印的顺序一定是123这样的顺序。
以上是“Java中怎么保证线程顺序执行”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。