Java多线程与并发编程 | Condition、Callable&Future

433 阅读3分钟

这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战

前言

每天积累一点点,积跬步至千里。今天学习JUC包中的Condition、Callable&Future。

Condition等待与唤醒

  • JUC提供了这么多的工具类,目的是让我们对于程序中多线程环境下,对线程的控制,Conditon对象,用来让指定的线程等待与唤醒,按照我们预期的顺序执行,大白话就是,我们完全掌控线程的执行顺序。注意: 它必须和ReentrantLock重入锁配合使用。
  • Condition用于替代wait()/notify()方法
    • notify只能随机唤醒等待的线程,而Condition可以唤醒指定线程,这有利于更好的控制并发程序。

Condition核心方法

  • await()阻塞当前线程,直到signal()唤醒
  • signal()唤醒被await()的线程,从中断处继续执行
  • signalAll()唤醒所有被await()阻塞的线程

代码示例

  • 创建三个线程,分别打印123,为了效果明显加了1000ms睡眠,正常情况下按顺序打印肯定是123
  • 但是现在我们想要打印312,这时就可以利用Condition来实现控制,看下面代码
public class ConditionSample {

    public static void main(String[] args) throws InterruptedException {

        ReentrantLock lock = new ReentrantLock();
        Condition c1 = lock.newCondition();
        Condition c2 = lock.newCondition();

        new Thread(() -> {
            lock.lock();
            try {
                c1.await();//阻塞当前线程
                Thread.sleep(1000);
                System.out.println(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }).start();

        new Thread(() -> {
            lock.lock();
            try {
                c2.await();//阻塞当前线程
                Thread.sleep(1000);
                System.out.println(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }).start();

        new Thread(() -> {
            lock.lock();
            try {
                Thread.sleep(1000);
                System.out.println(3);
                c1.signal();
                c2.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }).start();

    }
}

image.png

我们通过ReentrantLock对象创建了2Condition对象c1c2.相当于两个标记锁,分别加在打印12的线程上,等3打印完成,再去控制先唤醒哪个,先唤醒哪个则哪个打印输出,实现预期效果。

Callable&Future

  • Callable和Runnable一样用来创建线程,区别在于Callable有返回值并且可以抛出异常
  • Future是一个接口。它用于表示异步计算的结果。提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。

代码示例

获取10000以内所有的质数,单线程循环查找的做法这里就不做介绍了,看下面示例代码,我们可以通过多线程的方式去判断是否为质数,然后将结果接收返回。这里获取质数的方法比较简单,如果换成更为复杂的逻辑,多线程的效率优势就会很明显了。

public class FutureSample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 2; i <= 10000; i++) {
            Computor c = new Computor();
            c.setNum(i);
            Future<Boolean> result = executorService.submit(c);
            try {
                Boolean r = result.get();
                if (r) {
                    System.out.println(c.getNum());
                }
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        executorService.shutdown();
    }
}

class Computor implements Callable<Boolean> {
    private Integer num;
    public Integer getNum() {
        return num;
    }
    public void setNum(Integer num) {
        this.num = num;
    }
    @Override
    public Boolean call() throws Exception {
        boolean isprime = true;
        for (int i = 2; i < num; i++) {
            if (num % i == 0) {
                isprime = false;
                break;
            }
        }
        return isprime;
    }
}
  • Future是对用于计算的线程进行监听,因为计算是在其他线程中执行的,所以这个返回结果的过程是异步的
  • executorService.submit(c);c对象提交给线程池,如有空闲线程立即执行里面的call方法
  • result.get();用于获取返回值,如果线程内部的call没有执行完成,则进入等待状态,直到计算完成