农行1面:Java如何保证线程T1,T2,T3 顺序执行?

你好,我是猿java。

线程是 Java执行的最小单元,通常意义上来说,多个线程是为了加快速度且无需保序,这篇文章,我们来分析一道农业银行的面试题目:如要保证线程T1, T2, T3顺序执行?

考察意图

在面试中出现这道问题,通常是为了考察候选人的以下几个知识点:

1. 多线程基础知识: 希望了解候选人是否熟悉Java多线程的基本概念,包括线程的创建、启动和同步机制。

2. 同步机制的理解:候选人需要展示对Java中各种同步工具的理解,如join()CountDownLatchSemaphoreCyclicBarrier等,并知道如何在不同场景下应用这些工具。

3. 线程间通信:希望候选人理解线程间通信的基本原理,例如如何使用wait()notify()来协调线程。

4. 对Java并发包的熟悉程度: 希望候选人了解Java并发包(java.util.concurrent)中的工具和类,展示其对现代Java并发编程的掌握。

保证线程顺序执行的方法

在分析完面试题的考察意图之后,我们再分析如何保证线程顺序执行,这里列举了几种常见的方式。

join()

join()方法是Thread类的一部分,可以让一个线程等待另一个线程完成执行。 当你在一个线程T上调用T.join()时,调用线程将进入等待状态,直到线程T完成(即终止)。因此,可以通过在每个线程启动后调用join()来实现顺序执行。

如下示例代码,展示了join()如何保证线程顺序执行:

Thread t1 = new Thread(() -> {
   
});

Thread t2 = new Thread(() -> {
   
});

Thread t3 = new Thread(() -> {
   
});

t1.start();
t1.join(); 

t2.start();
t2.join(); 

t3.start();
t3.join(); 

CountDownLatch

CountDownLatch通过一个计数器来实现,初始时,计数器的值由构造函数设置,每次调用countDown()方法,计数器的值减1。当计数器的值变为零时,所有等待在await()方法上的线程都将被唤醒,继续执行。

CountDownLatch是Java并发包(java.util.concurrent)中的一个同步辅助类,用于协调多个线程之间的执行顺序。它允许一个或多个线程等待另外一组线程完成操作。

如下示例代码,展示了CountDownLatch如何保证线程顺序执行:

CountDownLatch latch1 = new CountDownLatch(1);
CountDownLatch latch2 = new CountDownLatch(1);

Thread t1 = new Thread(() -> {
   
   latch1.countDown(); 
});

Thread t2 = new Thread(() -> {
   try {
       latch1.await(); 
       
       latch2.countDown(); 
   } catch (InterruptedException e) {
       Thread.currentThread().interrupt();
   }
});

Thread t3 = new Thread(() -> {
   try {
       latch2.await(); 
       
   } catch (InterruptedException e) {
       Thread.currentThread().interrupt();
   }
});

t1.start();
t2.start();
t3.start();

CountDownLatch关键方法解析:

  • CountDownLatch(int count) : 构造函数,创建一个CountDownLatch实例,计数器的初始值为count。
  • void await() : 使当前线程等待,直到计数器的值变为零。
  • boolean await(long timeout, TimeUnit unit) : 使当前线程等待,直到计数器的值变为零或等待时间超过指定的时间。
  • void countDown() : 递减计数器的值。当计数器的值变为零时,所有等待的线程被唤醒。

Semaphore

Semaphore通过一个计数器来管理许可,计数器的初始值由构造函数指定,表示可用许可的数量。线程可以通过调用acquire()方法请求许可,如果许可可用则授予访问权限,否则线程将阻塞。使用完资源后,线程调用release()方法释放许可,从而允许其他阻塞的线程获取许可。

如下示例代码,展示了Semaphore如何保证线程顺序执行:

Semaphore semaphore1 = new Semaphore(0);
Semaphore semaphore2 = new Semaphore(0);

Thread t1 = new Thread(() -> {
   
   semaphore1.release(); 
});

Thread t2 = new Thread(() -> {
   try {
       semaphore1.acquire(); 
       
       semaphore2.release(); 
   } catch (InterruptedException e) {
       Thread.currentThread().interrupt();
   }
});

Thread t3 = new Thread(() -> {
   try {
       semaphore2.acquire(); 
       
   } catch (InterruptedException e) {
       Thread.currentThread().interrupt();
   }
});

t1.start();
t2.start();
t3.start();

Semaphore关键方法分析:

  • Semaphore(int permits) :构造一个具有给定许可数的Semaphore。
  • Semaphore(int permits, boolean fair) :构造一个具有给定许可数的Semaphore,并指定是否是公平的。公平性指的是线程获取许可的顺序是否是先到先得。
  • void acquire() :获取一个许可,如果没有可用许可,则阻塞直到有许可可用。
  • void acquire(int permits) :获取指定数量的许可。
  • void release() :释放一个许可。
  • void release(int permits) :释放指定数量的许可。
  • int availablePermits() :返回当前可用的许可数量。
  • boolean tryAcquire() :尝试获取一个许可,立即返回true或false。
  • boolean tryAcquire(long timeout, TimeUnit unit) :在给定的时间内尝试获取一个许可。

单线程池

单线程池(Executors.newSingleThreadExecutor())可以确保任务按提交顺序依次执行。所有任务都会在同一个线程中运行,保证了顺序性。

如下示例代码展示了单线程池如何保证线程顺序执行:

ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(new T1());
executor.submit(new T2());
executor.submit(new T3());
executor.shutdown();

单线程这种方法简单易用,适合需要顺序执行的场景。

synchronized

synchronized 是Java中的一个关键字,用于实现线程同步,确保多个线程对共享资源的访问是互斥的。它通过锁机制来保证同一时刻只有一个线程可以执行被Synchronized保护的代码块,从而避免数据不一致和线程安全问题。

如下示例代码,展示了synchronized如何保证线程顺序执行:

class Task {
    synchronized void executeTask(String taskName) {
        System.out.println(taskName + " 执行");
    }
}

public class Main {
    public static void main(String[] args) {
        Task task = new Task();
        new Thread(() -> task.executeTask("T1")).start();
        new Thread(() -> task.executeTask("T2")).start();
        new Thread(() -> task.executeTask("T3")).start();
    }
}

总结

本文,我们分析了5种保证线程T1,T2,T3顺序执行的方法,依次如下:

  1. join()
  2. CountDownLatch
  3. Semaphore
  4. 单线程池
  5. synchronized

在实际开发中,这种需要在业务代码中去保证线程执行顺序的情况几乎不会出现,因此,这个面试题其实缺乏实际的应用场景,纯粹是为了面试存在。尽管是面试题,还是可以帮助我们更好地去了解和掌握线程。

学习交流

如果你觉得文章有帮助,请帮忙转发给更多的好友,或关注公众号:猿java,持续输出硬核文章。

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=21175,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?