Java多线程

这里使用的方法是实现Callable接口。

首先要定义一个线程池来跑管理你的线程,不过注意这个线程池大小得注意一下,大小是有讲究的,有个公式就是:
最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目
比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么根据上面这个公式估算得到:((0.5+1.5)/0.5)*8=32;
比如说调DUBBO接口执行的多线程则是反过来(0.5/1.5+1)*8 = 10

是否使用线程池就一定比使用单线程高效呢?
答案是否定的,比如Redis就是单线程的,但它却非常高效,基本操作都能达到十万量级/s。从线程这个角度来看,部分原因在于:多线程带来线程上下文切换开销,单线程就没有这种开销;锁。

线程池的配置:private ExecutorService service可以通过Executors提供的静态方法获得线程池;
分别是。newCachedThreadPool、newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor。但是通过查询源码可以发现其实底层都是通过new ThreadPoolExector来实现的。

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

那么问题来了:ThreadPoolExecutor构造函数的几个参数是什么意思呢?
先看一下ThreadPoolExecutor的构造函数

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

-corePoolSize :线程池中所保存的核心线程数。线程池启动后默认是空的,只有任务来临时才会创建线程以处理请求。prestartAllCoreThreads方法可以在线程池启动后即启动所有核心线程以等待任务。(这个就是我们一般配的线程池的大小)

-maximumPoolSize:线程池允许创建的最大线程数。当workQueue使用无界队列时(如:LinkedBlockingQueue),则此参数无效。(一般采用无界队列新建线程池,这时候集合会拆成很多个线程一起放入线程池里。)

-keepAliveTime:当前线程池线程总数大于核心线程数时,终止多余的空闲线程的时间。(线程池里的线程很多的时候这个参数当然 要早点结束啦,设为0)

-unit:keepAliveTime参数的时间单位。

-workQueue:工作队列,如果当前线程池达到核心线程数时(corePoolSize),且当前所有线程都处于活动状态,则将新加入的任务放到此队列中。下面仅列几个常用的:

ArrayBlockingQueue:基于数组结构的有界队列,此队列按FIFO原则对任务进行排序。如果队列满了还有任务进来,则调用拒绝策略。

LinkedBlockingQueue:  基于链表结构的无界队列,此队列按FIFO(先入先出)原则对任务进行排序。因为它是无界的,根本不会满,所以采用此队列后线程池将忽略拒绝策略(handler)参数;同时还将忽略最大线程数(maximumPoolSize)等参数。(这个用的多点)

SynchronousQueue:直接将任务提交给线程而不是将它加入到队列,实际上此队列是空的。每个插入的操作必须等到另一个调用移除的操作;如果新任务来了线程池没有任何可用线程处理的话,则调用拒绝策略。其实要是把maximumPoolSize设置成无界(Integer.MAX_VALUE)的,加上SynchronousQueue队列,就等同于Executors.newCachedThreadPool()。

PriorityBlockingQueue: 具有优先级的队列的有界队列,可以自定义优先级;默认是按自然排序,可能很多场合并不合适。

-handler:拒绝策略,当线程池与workQueue队列都满了的情况下,对新加任务采取的策略。

AbortPolicy: 拒绝任务,抛出RejectedExecutionException异常。默认值。

CallerRunsPolicy: 

DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。这样的结果是最后加入的任务反而有可能被执行到,先前加入的都被抛弃了。

DiscardPolicy:加不进的任务都被抛弃了,同时没有异常抛出。

实际应用代码:

    private ExecutorService poolExecutorService = Executors.newFixedThreadPool(10);

定义一个监控线程数量的值,用来保证每个线程都跑完了

CountDownLatch begin = new CountDownLatch(size);

然后用Future 接收每个线程返回的数据

Future future = poolExecutorService.submit(new ProcessSingleTask(begin,list.get(i),client));
                ServiceResponse<ScoreUpdateResponse> response = (ServiceResponse<ScoreUpdateResponse>)future.get();
                scoreUpdateResponseList.add(response.getData());

最后一定要写上begin.await();这个就是之前用来监控线程是否跑完的代码,直到begin为0的时候才会认为线程池全部结束。

package com.galaxy.customer.business.impl.score;

import com.galaxy.customer.model.ServiceResponse;
import com.galaxy.customer.model.score.ScoreUpdateRequest;
import com.galaxy.customer.model.score.ScoreUpdateResponse;
import com.galaxy.foundation.model.Client;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * Created by fengyiming on 2016/9/13.
 */
public class ScoreUpdateThread {

    private ScoreUpdateManagerImpl scoreUpdateManager;

    private ExecutorService poolExecutorService = Executors.newFixedThreadPool(10);

    private Logger logger = Logger.getLogger(ScoreUpdateThread.class);

    public ServiceResponse<List<ScoreUpdateResponse>> updateForBatch(List<ScoreUpdateRequest> list,Client client) throws ExecutionException, InterruptedException{
        List<ScoreUpdateResponse> scoreUpdateResponseList = new ArrayList<ScoreUpdateResponse>(list.size());
        try {
            logger.error("------update data batch start----");
            int size = list.size();
            CountDownLatch begin = new CountDownLatch(size);
            for(int i = 0; i < size; i++) {
                logger.error("------" + i + " start------------------------");
                Future future = poolExecutorService.submit(new ProcessSingleTask(begin,list.get(i),client));
                ServiceResponse<ScoreUpdateResponse> response = (ServiceResponse<ScoreUpdateResponse>)future.get();
                scoreUpdateResponseList.add(response.getData());
                logger.error("------" + i + " end-------------------------");
            }
            begin.await();
            logger.error("------update data batch end----");
        }catch (Exception e){
            logger.error("--------thread error--------",e);
        }
        return ServiceResponse.succeess(scoreUpdateResponseList);
    }

    public void setScoreUpdateManager(ScoreUpdateManagerImpl scoreUpdateManager) {
        this.scoreUpdateManager = scoreUpdateManager;
    }

    /**
     * 带异步回调功能
     */
    public class ProcessSingleTask implements Callable<ServiceResponse<ScoreUpdateResponse>> {

        private CountDownLatch keepCount;
        private ScoreUpdateRequest request;
        private Client client;

        public ProcessSingleTask (CountDownLatch count,ScoreUpdateRequest request,Client client){
            this.keepCount = count;
            this.request = request;
            this.client = client;
        }

        public ServiceResponse<ScoreUpdateResponse> call() throws Exception {
            try {
                return scoreUpdateManager.updateUserScoreByManual(request,client);
            }catch (Exception e){
                logger.error("------update data error----",e);
            }finally {
                this.keepCount.countDown();
            }
            return null;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值