Java并发编程:Java线程(二)


往期

1.Java并发编程:Java线程(一)

同步与异步

以调用方角度来讲,如果

  • 需要等待结果返回,才能继续运行就是同步
  • 不需要等待结果返回,就能继续运行就是异步

需要等待结果

这时既可以使用同步处理,也可以使用异步来处理

1. join实现(同步)

static int result = 0;
    private static void test1() throws InterruptedException {
        log.debug("开始");
        Thread t1 = new Thread(() -> {
            log.debug("开始");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("结束");
            result = 10;
        }, "t1");
        t1.start();
        t1.join();
        log.debug("结果为:{}", result);
    }

输出

19:46:36.178 [main] DEBUG SyncAndAsync.TestSyncAndAsync - 开始
19:46:36.272 [t1] DEBUG SyncAndAsync.TestSyncAndAsync - 开始
19:46:37.294 [t1] DEBUG SyncAndAsync.TestSyncAndAsync - 结束
19:46:37.294 [main] DEBUG SyncAndAsync.TestSyncAndAsync - 结果为:10

评价

  • 需要外部共享变量,不符合面向对象封装的思想
  • 必须等待线程结束,不能配合线程池使用

2. Future实现(同步)

private static void test2() throws InterruptedException, ExecutionException {
    log.debug("开始");
    FutureTask<Integer> result = new FutureTask<>(() -> {
        log.debug("开始");
        Thread.sleep(1000);
        log.debug("结束");
        return 10;
    });
    new Thread(result, "t1").start();
    log.debug("结果为:{}", result.get());
}

输出

19:48:19.840 [main] DEBUG SyncAndAsync.TestSyncAndAsync - 开始
19:48:19.933 [t1] DEBUG SyncAndAsync.TestSyncAndAsync - 开始
19:48:20.934 [t1] DEBUG SyncAndAsync.TestSyncAndAsync - 结束
19:48:20.935 [main] DEBUG SyncAndAsync.TestSyncAndAsync - 结果为:10

评价

  • 规避了使用join之前的缺点
  • 可以方便配合线程池使用
private static void test3() throws ExecutionException, InterruptedException {
    ExecutorService service = Executors.newFixedThreadPool(1);
    log.debug("开始");
    Future<Integer> result = service.submit(()->{
        log.debug("开始");
        Thread.sleep(1000);
        log.debug("结束");
        return 10;
    });
    log.debug("结果:{},result的类型:{}",result.get(), result.getClass());
    service.shutdown();
}

输出

19:53:07.072 [main] DEBUG SyncAndAsync.TestSyncAndAsync - 开始
19:53:07.177 [pool-1-thread-1] DEBUG SyncAndAsync.TestSyncAndAsync - 开始
19:53:08.177 [pool-1-thread-1] DEBUG SyncAndAsync.TestSyncAndAsync - 结束
19:53:08.177 [main] DEBUG SyncAndAsync.TestSyncAndAsync - 结果:10,result的类型:class java.util.concurrent.FutureTask

评价

  • 仍然是main线程接收结果
  • get方法是让调用线程同步等待

3. 自定义实现-保护性暂停(同步)

  1. 定义

即 Guarded Suspension, 用在一个线程等待另一个线程的执行结果

要点

  • 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject
  • 如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)
  • JDK中,join的实现、Future的实现,采用的就是此模式
  • 因为要等待另一方的结果,因此归类到同步模式

在这里插入图片描述

  1. 实现(带超时版)
class GuardedObject{
    // 结果
    private Object response;

    private static final Logger log = LoggerFactory.getLogger(GuardedObject.class);

    // 获取结果
    // timeout 表示要等待多久
    public Object getResponse(long timeout) throws InterruptedException {
        synchronized (this) {
            // 开始时间
            long begin = System.currentTimeMillis();
            // 经历时间
            long passedTime = 0;
            // 没有结果
            while (response == null) {
                long waitTime = timeout - passedTime;     // 这一轮循环应该等待的时间
                // 经历的时间超过了最大等待时间就退出循环
                if (waitTime <= 0) {
                    break;
                }
                this.wait(waitTime);
                passedTime = System.currentTimeMillis() - begin;
                log.debug("timePassed:{}, object is null {}", passedTime, response == null);
            }
            return response;
        }
    }

    // 产生结果
    public void setResponse(Object response) {
        synchronized (this) {
            // 给结果成员变量赋值
            this.response = response;
            this.notifyAll();
        }
    }
}

应用:一个线程等待另一个线程的执行结果

public class Downloader {
    public static List<String> download() throws IOException {
        HttpURLConnection connection = (HttpURLConnection) new URL("https://www.baidu.com/").openConnection();
        List<String> lines = new ArrayList<>();
        try (BufferedReader reader =
                new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line =reader.readLine()) != null) {
                lines.add(line);
            }
        }
        return lines;
    }
}

public static void main(String[] args) {
        test1();
}
public static void test1() {
    // t1等待t2 的下载结果
    final GuardedObject guardedObject = new GuardedObject();
    new Thread(()->{
        // 等待结果
        log.debug("等待结果");
        try {
            List<String> response = (List<String>)guardedObject.getResponse(2000);
            log.debug("结果大小:{}",response.size());
            //log.debug("{}",response);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    },"t1").start();

    new Thread(()->{
        log.debug("执行下载");
        try {
            List<String> list = Downloader.download();
            guardedObject.setResponse(list);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }, "t2").start();
}

执行结果

20:32:14.327 [t2] DEBUG Share.GuardedSuspension4_8.TestGuardSuspension - 执行下载
20:32:14.327 [t1] DEBUG Share.GuardedSuspension4_8.TestGuardSuspension - 等待结果
20:32:15.332 [t1] DEBUG Share.GuardedSuspension4_8.GuardedObject - timePassed:1000, object is null false
20:32:15.335 [t1] DEBUG Share.GuardedSuspension4_8.TestGuardSuspension - 结果大小:3

4. CompletableFuture实现(异步)

private static void test4() {
    // 进行计算的线程池
    ExecutorService computeService = Executors.newFixedThreadPool(1);
    // 接收结果的线程池
    ExecutorService resultService = Executors.newFixedThreadPool(1);
    log.debug("开始");
    CompletableFuture.supplyAsync(() -> {
        log.debug("开始");
        try {
        	Thread.sleep(1000);
        } catch (InterruptedException e) {
        	e.printStackTrace();
        }
        log.debug("结束");
        return 10;
    }, computeService).thenAcceptAsync((result) -> {
    	log.debug("结果为:{}", result);
    }, resultService);
}

输出

20:36:46.292 [main] DEBUG SyncAndAsync.TestSyncAndAsync - 开始
20:36:46.392 [pool-1-thread-1] DEBUG SyncAndAsync.TestSyncAndAsync - 开始
20:36:47.408 [pool-1-thread-1] DEBUG SyncAndAsync.TestSyncAndAsync - 结束
20:36:47.409 [pool-2-thread-1] DEBUG SyncAndAsync.TestSyncAndAsync - 结果为:10

评价

  • 可以让调用线程异步处理结果,实际是其他线程去同步等待
  • 可以方便地分离不同职责的线程池
  • 以任务为中心,而不是以线程为中心

5. BlockingQueue实现(异步)

private static void test5() {
    ExecutorService consumer = Executors.newFixedThreadPool(1);
    ExecutorService producer = Executors.newFixedThreadPool(1);
    SynchronousQueue<Integer> queue = new SynchronousQueue<>();
    log.debug("开始");
    producer.submit(()->{
        log.debug("开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.debug("结束");
        try {
            queue.put(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    consumer.submit(()->{
        Integer res = null;
        try {
            res = queue.take();
            log.debug("结果为:{}",res);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    consumer.shutdown();
    producer.shutdown();
}

输出结果

20:45:42.931 [main] DEBUG SyncAndAsync.TestSyncAndAsync - 开始
20:45:43.018 [pool-2-thread-1] DEBUG SyncAndAsync.TestSyncAndAsync - 开始
20:45:44.018 [pool-2-thread-1] DEBUG SyncAndAsync.TestSyncAndAsync - 结束
20:45:44.018 [pool-1-thread-1] DEBUG SyncAndAsync.TestSyncAndAsync - 结果为:10

不需要等待结果

Reference

全面深入学习java并发编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xylitolz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值