Java同步--Callable和Runnable同步异步的使用

Callable和Runnable同步异步的使用

Runnable我们都用过,因为开启一个新的线程的时候,需要传递一个Runnable接口作为执行对象,这个接口里面只包含一个run方法,无参数,无返回值。

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

Callable和Runnable类似,但是有返回值。Callable里面只有一个方法:

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

execute方法

首先介绍ExecutorService类的execute方法,该方法来自于父类Executor的方法,入参为Runnable,无返回值。所以该方法适用于开启线程做异步操作,无需等待返回结果及超时时间。

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

 使用如下:

@Value("${detailThreadPool.sync.corePoolSize:20}")
private int syncCorePoolSize;
@Value("${detailThreadPool.sync.maximumPoolSize:250}")
private int syncMaximumPoolSize;
@Value("${detailThreadPool.sync.keepAliveTime:2000}")
private int syncKeepAliveTime;
@Value("${detailThreadPool.sync.queueSize:100}")
private int syncQueueSize;

private ExecutorService asyncExecutor;

@PostConstruct
public void init() {
    asyncExecutor = new ThreadPoolExecutor(asyncCorePoolSize, asyncMaximumPoolSize, asyncKeepAliveTime, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<>(asyncQueueSize),
            new ThreadFactory() {
                AtomicInteger count = new AtomicInteger(0);

                public Thread newThread(Runnable r) {
                    Thread th = new Thread(r, "asyncUserDetail-" + count.getAndIncrement());
                    return th;
                }
            },
            new ThreadPoolExecutor.AbortPolicy());
}

// ExecutorService异步处理,具体操作可以写业务类继承Runnable接口,并在构造方法传入具体业务参数等等
public void asyncExecutor(){
    asyncExecutor.execute(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < 1000; i++) {
                System.out.println(i);
            }
        }
    });
    asyncExecutor.shutdown();
}

submit方法

submit属于ExecutorService类的方法,有两个构造法方法,一个用来提交Runnable对象,一个用来提交Callable对象。这些方法都是返回一个Future的对象。所以该方法适用于开启线程做同步操作

我们可以来看一下:

public interface ExecutorService extends Executor {
  @NotNull <T> Future<T> submit(@NotNull Callable<T> task);
  @NotNull <T> Future<T> submit(@NotNull Runnable task,T result);
  @NotNull Future<?> submit(@NotNull Runnable task)
}

使用如下:

@Value("${detailThreadPool.sync.corePoolSize:20}")
private int syncCorePoolSize;
@Value("${detailThreadPool.sync.maximumPoolSize:250}")
private int syncMaximumPoolSize;
@Value("${detailThreadPool.sync.keepAliveTime:2000}")
private int syncKeepAliveTime;
@Value("${detailThreadPool.sync.queueSize:100}")
private int syncQueueSize;

private ExecutorService syncExecutor;

@PostConstruct
public void init() {
    syncExecutor = new ThreadPoolExecutor(syncCorePoolSize, syncMaximumPoolSize, syncKeepAliveTime, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<>(syncQueueSize),
            new ThreadFactory() {
                AtomicInteger count = new AtomicInteger(0);

                public Thread newThread(Runnable r) {
                    Thread th = new Thread(r, "syncUserDetail-" + count.getAndIncrement());
                    return th;
                }
            },
            new ThreadPoolExecutor.AbortPolicy());
}
// ExecutorService同步处理,具体操作可以写业务类继承Callable接口,并在构造方法传入具体业务参数等等
public void syncExecutor() throws Exception {
    Future<Integer> sumFuture = syncExecutor.submit(new Callable<Integer>() {
        @Override
        public Integer call() {
            int sum = 0;
            for (int i = 0; i < 100; i++) {
                sum += i;
            }
            return sum;
        }
    });
// 此处主线程可以先处理其他事情,在来调用.get()方法,处理异步结果的内容。
    System.out.println(sumFuture.get());
}

注意:

向线程池中提交任务的submit方法不是阻塞方法,Future.get方法是一个阻塞方法,当submit提交多个任务时,只有所有任务都完成后,才能使用get按照任务的提交顺序得到返回结果,所以一般需要使用future.isDone先判断任务是否全部执行完成,完成后再使用future.get得到结果。(也可以用get (long timeout, TimeUnit unit)方法可以设置超时时间,防止无限时间的等待)

三段式的编程:1.启动多线程任务2.处理其他事3.收集多线程任务结果,Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,要么使用阻塞,在future.get()的地方等待future返回的结果,这时又变成同步操作;要么使用isDone()轮询地判断Future是否完成,这样会耗费CPU的资源。

FutureCompletionServiceCompletableFuture区别可以参考文章:future.get方法阻塞问题的解决,实现按照任务完成的先后顺序获取任务的结果_fourierr的博客-CSDN博客_future.get

其中Future为同步阻塞、CompletionService为同步非阻塞、CompletableFuture为异步非阻塞

CompletableFuture创建异步任务的两种方式:

(1)创建任务

##使用runAsync方法新建一个线程来运行Runnable对象(无返回值);

        CompletableFuture.runAsync(() -> {
            List<Long> seqList = list.getRecords().stream().filter(e -> !e.getIsRead()).map(e -> e.getSeqId()).collect(Collectors.toList());
            if (seqList.size() > 0) {
                log.info(">>>>>> 异步更新未读记录");
                batchSetRecordIsRead(seqList);
                log.info("<<<<<< 异步更新未读记录完成,size:{}", seqList.size());
            }
        });

##使用supplyAysnc方法新建线程来运行Supplier<T>对象(有返回值);

CompletableFuture<Integer> result = CompletableFuture.supplyAsync(() -> {
            List<Long> seqList = list.getRecords().stream().filter(e -> !e.getIsRead()).map(e -> e.getSeqId()).collect(Collectors.toList());
            if (seqList.size() > 0) {
                log.info(">>>>>> 异步更新未读记录");
                batchSetRecordIsRead(seqList);
                log.info("<<<<<< 异步更新未读记录完成,size:{}", seqList.size());
            }
            return 5;
        });

##基于线程池创建

Future总结(同步、异步使用)

同步可以用submit方法:

  • 原理,主线程阻塞等待线程池执行结果,从共享变量获取结果
  • 使用场景
    • 需要程序提高执行速度,并且需要获取执行结果
    • 需要有超时等待任务执行完毕,超时获取结果

异步可以用execute方法:

       只需要提交一个任务(不需要超时等待),且主线程(main)提交任务后无操作

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java同步异步的实现可以通过多线程来实现。 1. 同步同步是指多个线程按照一定的顺序执行,每个线程在执行完自己的任务之前必须等待其他线程完成其任务。在Java中,可以使用关键字synchronized来实现同步。synchronized关键字用于修饰方法或代码块,保证在同一时间只有一个线程可以执行被synchronized修饰的代码。 例如,下面的代码演示了一个计数器的同步实现: ```java public class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } } // 使用方式: Counter counter = new Counter(); counter.increment(); // 同步增加计数器的值 int count = counter.getCount(); // 同步获取计数器的值 ``` 2. 异步异步是指多个线程可以同时执行,不需要等待其他线程的完成。在Java中,可以使用多线程和回调函数来实现异步操作。Java提供了多种实现异步编程的方式,如使用Thread类、Runnable接口、Callable接口、Future接口、CompletableFuture类等。 例如,下面的代码演示了使用Thread类实现异步操作: ```java public class AsyncExample { public static void main(String[] args) { Thread thread = new Thread(() -> { // 异步执行的代码块 System.out.println("异步操作开始"); // ... System.out.println("异步操作结束"); }); thread.start(); // 主线程不需要等待异步操作完成就可以继续执行 System.out.println("主线程继续执行"); } } ``` 在实际开发中,根据具体的需求和场景选择合适的同步异步实现方式。同步操作适用于需要保证线程安全和顺序执行的场景,而异步操作适用于提高系统的并发性能和响应速度的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值