面试1: 解决线程顺序有几种方式

简介: 面试1: 解决线程顺序有几种方式


解决线程顺序有几种方式

其实,这个问题比较简单.主要考察对 join方法的掌握。

来了解下join

thread.join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。

如:在线程B中调用了线程A的join方法,只有A执行完毕,才会继续执行线程B。

t.join(); //调用join方法,等待线程执行完毕
t.join(1000); //等待t线程,等待时间是1000毫秒。

join的原理

任何地方当调用了t.join(),就必须要等待线程t执行完毕后,才能继续执行其他线程。这里其实是运用了Java中最顶级对象Object提供的方法wait()。wait()方法用于线程间通信,它的含义是通知一个线程等待一下,让出CPU资源,注意这里是会放弃已经占有的资源的。直到t线程执行完毕,再调用notify()唤醒当前正在运行的线程。

一般来说,可以用三种方式:

1. 使用join

例子,具体代码:

先准备3个线程,然后在线程中,调动另一个线程的join方法。

public class JoinDemo {
     //main方法测试
    public static void main(String[] args) {
        //线程1
        final Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " run 1");
            }
        }, "T1");
        //线程2
        final Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                //t1想插队,t1执行完,t2才能执行
                    t1.join();
                    System.out.println(Thread.currentThread().getName() + " run 2");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "T2");
        //线程3
        final Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                   //t2想插队,t2执行完,t3才能执行
                    t2.join();                    System.out.println(Thread.currentThread().getName() + " run 3");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "T3");
        t1.start();
        t2.start();
        t3.start();
        System.out.println("-----------------------");
    }
}

执行结果:

看结果的确是,按照顺序执行。

2:单个线程池newSingleThreadExecutor

上面的案例改造下:

public class Join2Demo {
    public static void main(String[] args) {
        final Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " run 1");
            }
        }, "T1");
        final Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    t1.join();
                    System.out.println(Thread.currentThread().getName() + " run 2");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "T2");
        final Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    t2.join();
                    System.out.println(Thread.currentThread().getName() + " run 3");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "T3");
//        // 线程池写法2 
//        ExecutorService executor = Executors.newSingleThreadExecutor();
//         executor.submit(t1);
//         executor.submit(t2);
//         executor.submit(t3);
//         executor.shutdown();
        //线程池写法2 (阿里推荐写法) 手动创建线程池,让开发者了解线程的基本信息,便于控制
        ExecutorService pool = new ThreadPoolExecutor(1, 1,
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(2),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        pool.execute(t1);
        pool.execute(t2);
        pool.execute(t3);
        pool.shutdown();//gracefully shutdown
    }
}

运行结果:

结果和预期一样。

3: 锁+共享变量

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class OrderDemo {
    private static Lock lock  = new ReentrantLock();
    private static int state = 0;
    //线程1        
    static class ThreadA extends Thread{
        @Override
        public void run(){
            for (int i = 0; i < 10; ) {
                lock.lock();
                try {
                    if (state % 3 == 0){
                        System.out.println(Thread.currentThread().getName() + "A");
                        state++;
                        i++;
                    }
                }finally {
                    lock.unlock();
                }
            }
        }
    }
  //线程2     
    static class ThreadB extends Thread{
        @Override
        public void run(){
            for (int i = 0; i < 10; ) {
                lock.lock();
                try {
                    if (state % 3 == 1){
                        System.out.println(Thread.currentThread().getName() + "B");
                        state++;
                        i++;
                    }
                }finally {
                    lock.unlock();
                }
            }
        }
    }
  //线程3   
    static class ThreadC extends Thread{
        @Override
        public void run(){
            for (int i = 0; i < 10; ) {
                lock.lock();
                try {
                    if (state % 3 == 2){
                        System.out.println(Thread.currentThread().getName() + "C");
                        state++;
                        i++;
                    }
                }finally {
                    lock.unlock();
                }
            }
        }
    }
    public static void main(String[] args) {
        new ThreadA().start();
        new ThreadB().start();
        new ThreadC().start();
    }
}

运行结果如下:

可以看到是按照顺序执行的奥。

再来个锁的例子

/**
 * @version 1.0
 * @date 2021/5/16
 * Lock提供条件Condition,对线程的等待和唤醒更加详细和灵活
 *
 * 内部维护一个Condition队列。当前线程调用await方法后,将会以当前线程构造为一个结点Node,并将该节点放到该队列的尾部
 *
 * Condition是个接口,基本的方法就是await()和signal()方法;
 * Condition依赖于Lock接口,生成一个Condition的方式是lock.newCondition()
 */
public class Concurrent03 {
    private int count;
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    public static void main(String[] args) {
        Concurrent03 c = new Concurrent03();
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(c::printA);
        service.execute(c::printB);
        service.execute(c::printC);
        service.shutdown();
    }
    public void printA() {
        lock.lock();
        try {
            while (true){
                if (count % 3 != 0) {
                    condition.await();
                }
                Thread.sleep(300);
                System.out.print("A ");
                count ++;
                condition.signalAll();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB() {
        lock.lock();
        try {
            while (true){
                if (count % 3 != 1) {
                    condition.await();
                }
                Thread.sleep(300);
                System.out.print("B ");
                count ++;
                condition.signalAll();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC() {
        lock.lock();
        try {
            while (true){
                if (count % 3 != 2) {
                    condition.await();
                }
                Thread.sleep(300);
                System.out.print("C ");
                count ++;
                condition.signalAll();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

运行结果:

可以看到按顺序打印了 A B C …


相关文章
|
24天前
|
Java 数据库连接 调度
面试题:用过线程池吗?如何自定义线程池?线程池的参数?
字节跳动面试题:用过线程池吗?如何自定义线程池?线程池的参数?
27 0
|
2月前
|
Java 程序员
java线程池讲解面试
java线程池讲解面试
62 1
|
2月前
|
安全 Java
Qt经典面试题:Qt开启线程的几种方式
Qt经典面试题:Qt开启线程的几种方式
25 0
|
2月前
|
并行计算 安全 Java
C# .NET面试系列四:多线程
<h2>多线程 #### 1. 根据线程安全的相关知识,分析以下代码,当调用 test 方法时 i > 10 时是否会引起死锁? 并简要说明理由。 ```c# public void test(int i) { lock(this) { if (i > 10) { i--; test(i); } } } ``` 在给定的代码中,不会发生死锁。死锁通常是由于两个或多个线程互相等待对方释放锁而无法继续执行的情况。在这个代码中,只有一个线程持有锁,且没有其他线程参与,因此不
106 3
|
3天前
|
监控 安全 Java
【多线程学习】深入探究阻塞队列与生产者消费者模型和线程池常见面试题
【多线程学习】深入探究阻塞队列与生产者消费者模型和线程池常见面试题
|
9天前
|
Java 调度
Java面试必考题之线程的生命周期,结合源码,透彻讲解!
Java面试必考题之线程的生命周期,结合源码,透彻讲解!
38 1
|
9天前
|
Java
面试官让说出8种创建线程的方式,我只说了4种,然后挂了。。。
面试官让说出8种创建线程的方式,我只说了4种,然后挂了。。。
12 1
|
15天前
|
调度 Python
Python多线程、多进程与协程面试题解析
【4月更文挑战第14天】Python并发编程涉及多线程、多进程和协程。面试中,对这些概念的理解和应用是评估候选人的重要标准。本文介绍了它们的基础知识、常见问题和应对策略。多线程在同一进程中并发执行,多进程通过进程间通信实现并发,协程则使用`asyncio`进行轻量级线程控制。面试常遇到的问题包括并发并行混淆、GIL影响多线程性能、进程间通信不当和协程异步IO理解不清。要掌握并发模型,需明确其适用场景,理解GIL、进程间通信和协程调度机制。
30 0
|
21天前
|
存储 安全 Java
多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)(下)
多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)(下)
43 0
|
21天前
|
存储 安全 Java
多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)(上)
多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)
35 0

相关实验场景

更多