多线程与高并发线程池

了解线程池之前咱们先了解一个概念并发与并行。

并发:多用户(多线程)执行任务
并行:多线程执行任务
区别:
	有无用户参与

了解多线程首先咱们先了解下线程池的分类

线程池分为两类:
	ThreadPoolExecutor:管理线程的容器技术
	ForkJoinPool:对任务分解与合并

为什么使用多线程池呢?
不了解线程的可以看下这篇文章

咱们都知道,多线程大多应用场景下能够提高程序的运行效率,是调度执行的基本单位,多个线程共享资源。
但是线程的启动和销毁很耗时的,这就造成了咱们执行任务的时候大量的时间在开启与销毁线程。
怎么解决呢?
这么大技术漏洞jdk早就帮咱们想好了,使用线程池管理技术。
线程池:故名思意,管理线程的池子。

ThreadPoolExecutor

线程池的简单使用

线程池简单使用

public static void main(String[] args) throws Exception{
    LinkedBlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(2);
    ExecutorService executor =
            new ThreadPoolExecutor(2,
                    4,
                    10,
                    TimeUnit.SECONDS,
                    blockingQueue,
                    new TestMain.NamedThreadFactory(),
                    new ThreadPoolExecutor.CallerRunsPolicy()
            );
    executor.execute(()->System.out.println("无法返回值的线程运行了..."+Thread.currentThread().getName()));
    Future<String> submit = executor.submit(() -> {
        System.out.println("有法返回值的线程运行了..." + Thread.currentThread().getName());
        return "OK";
    });
    String s = submit.get();
    System.out.println("有返回值的线程返回值="+s);
    executor.shutdown();
}

static class NamedThreadFactory implements ThreadFactory {
    private AtomicInteger atomicInteger = new AtomicInteger();
    @Override
    public Thread newThread(Runnable r) {
        atomicInteger.incrementAndGet();
        Thread thread =  new MyWorkThread(atomicInteger,r);
        return thread;
    }
}
static class MyWorkThread extends Thread{
    private AtomicInteger atomicInteger;

    public MyWorkThread(AtomicInteger atomicInteger,Runnable runnable){
        super(runnable);
        this.atomicInteger = atomicInteger;
    }
    @Override
    public void run() {
        super.run();
    }
}

ThreadPoolExecutor 构造

Executors也定义特定参数的线程池,不过它们最终还是调用的这个构造,所以咱们只要了解了这个构造,其他的都自然明白了。

ThreadPoolExecutor(int corePoolSize,//核心线程数
                   int maximumPoolSize,//最大线程数
                   long keepAliveTime,//存活时间
                   TimeUnit unit,//存活时间单位
                   BlockingQueue<Runnable> workQueue,//任务队列
                   ThreadFactory threadFactory,//线程工厂
                   RejectedExecutionHandler handler//拒绝策略
                  )

Executors工具定义的几种线程池

//缓存线程池,最大能缓存Integer.MAX_VALUE(适合并发波动较大的场景)
ExecutorService cachedService = Executors.newCachedThreadPool();
//固定的线程池 核心数和最大线程数是固定的(适合并发比较稳定的场景)
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
//单线程的线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
//周期性操作线程池
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);

ThreadPoolExecutor重要成员

咱们看图了解下重要的成员
在这里插入图片描述
线程池状态与线程池数量

//ctl 有两个作用,1、标识线程池的状态高三位 2、线程池的数量低28位
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//把线程状态和线程池数量得高位和低位临界值
private static final int COUNT_BITS = Integer.SIZE - 3;
//线程池的容量(正数高位必须是0)
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// 线程池的运行状态
// 接收新的任务,处理队列中的任务
private static final int RUNNING    = -1 << COUNT_BITS;
//不接收新任务,处理队列中的任务
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// 不接收新任务,不处理队列任务,并且中断进行中的任务
private static final int STOP       =  1 << COUNT_BITS;
//所有任务都终止,线程池中任务为0,将运行状态的线程过渡到终止状态
private static final int TIDYING    =  2 << COUNT_BITS;
//终止状态
private static final int TERMINATED =  3 << COUNT_BITS;

// 前面说了,ctl 包括两部分内容,这些方法就是辅助功能
//根据ctl查看线程池的状态
private static int runStateOf(int c)     { return c & ~CAPACITY; }
//根据 ctl查看线程池中的任务数量
private static int workerCountOf(int c)  { return c & CAPACITY; }
//设置线程池对应的状态
private static int ctlOf(int rs, int wc) { return rs | wc; }

ThreadPoolExecutor.execute()方法

接下来了解下execute方法是如何实现的

//提交任务
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    //获取ctl
    int c = ctl.get();
    //如果线程池中的数量小于核心线程数,说明需要增加核心线程
    if (workerCountOf(c) < corePoolSize) {
        //如果增加线程成功直接返回
        if (addWorker(command, true))
            return;
        //如果增加失败了,重新获取ctl
        c = ctl.get();
    }
    //如果线程池是运行中的状态,并且添加工作线程成功
    if (isRunning(c) && workQueue.offer(command)) {
        //再次获取ctl用于检查
        int recheck = ctl.get();
        //如果不是运行中的状态 并且 删除成功了 执行拒绝策略,这里主要是解决高并发下带来的问题
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            //如果workerQueue中无task,直接添加worker
            addWorker(null, false);
    }
    //如果不是运行中的状态 或者添加任务队列失败了,尝试增加新任务(最大新任务个数为MaxPoolSize)
    //添加非核心线程
    else if (!addWorker(command, false))
        //如果添加失败(最大线程数-核心线程数的线程),执行拒绝策略
        reject(command);
}

ThreadPoolExecutor.addWorker(Runnable firstTask, boolean core)方法

//添加任务,core 是否是核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
    //死循环标识
    retry:
    for (;;) {
        //获取ctl
        int c = ctl.get();
        //获取线程池的状态
        int rs = runStateOf(c);

        // 验证线程池是否满足添加新任务
        //如果线程池处于不接收任务,不处理任务之后的状态并且如果线程池状态不是 SHUTDOWN 或者 任务不为null 或者 workQueue是空的
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;
        //对工作任务数量进行+1操作
        for (;;) {
            //获取线程池的工作线程数量
            int wc = workerCountOf(c);
            //如果超出最大容量 或者 大于等于核心任务数量或者最大任务数量
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                //不需要添加worker
                return false;
            //对工作任务数量进行+1操作
            if (compareAndIncrementWorkerCount(c))
                break retry;
            //如果对工作任务+1操作失败,则重新获取ctl
            c = ctl.get();
            //如果线程池的状态改变了,则重新进行添加任务之前的验证操作
            if (runStateOf(c) != rs)
                continue retry;
        }
    }
    //worker是否运行
    boolean workerStarted = false;
    //worker是否被添加
    boolean workerAdded = false;
    //线程池中最大的worker数量为maximumPoolSize
    Worker w = null;
    try {
        //创建任务
        w = new Worker(firstTask);
        //这个线程是执行worker的线程,而firstTask是执行咱们代码线程
        final Thread t = w.thread;
        if (t != null) {
            //获取到同步对象
            final ReentrantLock mainLock = this.mainLock;
            //加锁
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                //重新检查线程池的状态
                int rs = runStateOf(ctl.get());
                //如果线程池是运行状态或者是 关系状态并且任务为null
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    //这里必须要验证下新创建的work不能是运行中
                    if (t.isAlive())
                        throw new IllegalThreadStateException();
                    //把work添加到HashSet,这个workers就是线程池的核心线程
                    //工作线程是放在BlockingQueue中
                    workers.add(w);
                    int s = workers.size();
                    //对创建的worker进行记录
                    //largestPoolSize不是Automic的成员是不是存在线程安全?不会的,因为是被主锁锁定中,线程安全
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    //标志添加成功
                    workerAdded = true;
                }
            } finally {
                //释放同步锁
                mainLock.unlock();
            }
            //如果添加任务成功了,需要启动worker,只有启动worker之后,工作线程才能被资源进行调度
            if (workerAdded) {
                //启动worker线程,后台执行task
                t.start();
                //表示worker启动成功
                workerStarted = true;
            }
        }
    } finally {
        //如果worker启动失败
        if (! workerStarted)
            //删除HashSet中的worker,worker数再减1,尝试关闭线程
            addWorkerFailed(w);
    }
    return workerStarted;
}

ThreadPoolExecutor.runWorker(Worker w)方法

执行worker,并且执行当前线程任务task;worker执行完当前任务之后会去workQueue中获取task,然后一直循环执行。如果不获取不到任务,并且worker超过生存时间就会被线程池回收

final void runWorker(Worker w) {
    //获取当前线程
    Thread wt = Thread.currentThread();
    //获取到worker中的线程
    Runnable task = w.firstTask;
    //清空任务,下次好从任务队列中拿任务
    w.firstTask = null;
    //允许中断
    w.unlock(); 
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            w.lock();
            //满足条件中断worker
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    //执行咱们自己的代码逻辑
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                //线程运行完成,把任务
                task = null;
                //记录这个worker执行的线程数
                w.completedTasks++;
                //释放同步资源
                w.unlock();
            }
        }
        //完成所有的任务,没有或者获取不到可执行的任务
        completedAbruptly = false;
    } finally {
        //退出当前worker
        processWorkerExit(w, completedAbruptly);
    }
}

ThreadPoolExecutor.runWorker(Worker w)方法

从workQueue中获取task执行,多个worker并发获取

private Runnable getTask() {
    //超时标志
    boolean timedOut = false;
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        //如果任务队列为空或者线程池是不执行任务的线程的状态,直接返回null
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        //获取workerQueue中线程线程数
        int wc = workerCountOf(c);
        //
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        //判断是否需要从新获取或者关闭当前worker
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            //获取workerQueue中task
            Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                //获取到任务后直接返回
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

RejectedExecutionHandler

拒绝策略很简单,大家有兴趣稍微看看就行了,大部分使用都是自定义拒绝策略

AbortPolicy:(默认拒接策略)丢掉任务,抛出异常
DiscardPolicy:丢掉任务,什么也不做
DiscardOldestPolicy:删除掉最早加入的任务,尝试再次加入
CallerRunsPolicy:主线程执行这个任务

简单的画个图吧

在这里插入图片描述

ForkJoinPool

咱们看张图了解下ForkJoinPool执行流程
从图中可以看出对大任务进行切分然后进行结果汇聚
在这里插入图片描述

简单使用

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;

public class ForkJoinPoolTest {
	static int[] nums = new int[1000000];
	static final int max_num = 50000;
	static Random r = new Random();
	static {
		long start = System.currentTimeMillis();
		for(int i = 0; i < nums.length; i++) {
			nums[i] = r.nextInt(100);
		}
		System.out.println(Arrays.stream(nums).sum());
		long end = System.currentTimeMillis();
		System.out.println(end-start);
	}
	static class AddTask extends RecursiveAction{
		int start,end;
		AddTask(int start,int end){
			this.start = start;
			this.end = end;
		}
		@Override
		protected void compute() {
			if((end - start) <= max_num) {
				long sum = 0L;
				for(int i = start;i<end;i++) {
					sum += nums[i];
				}
				System.out.println("from:" + start + " to:" + end + " = " + sum);
			}else {
				//进行切分任务 具体切分规则就是在这里定义
				int mid = start + (end - start)/2;
				AddTask task1 = new AddTask(start, mid);
				AddTask task2 = new AddTask(mid, end);
				task1.fork();
				task2.fork();
				task1.join();
				task2.join();
			}
		}
	}
	
	static class AddTaskRes extends RecursiveTask<Long>{
		private static final long serialVersionUID = 1L;
		int start,end;
		AddTaskRes(int start,int end){
			this.start = start;
			this.end = end;
		}
		@Override
		protected Long compute() {
			if(end - start <= max_num) {
				long sum = 0L;
				for(int i = start;i<end;i++) {
					sum += nums[i];
				}
				return sum;
			}
			//进行切分任务 具体切分规则就是在这里定义
			int mid = start + (end - start)/2;
			AddTaskRes task1 = new AddTaskRes(start, mid);
			AddTaskRes task2 = new AddTaskRes(mid, end);
			task1.fork();
			task2.fork();
			return task1.join() + task2.join();
		}
	}
	public static void main(String[] args) throws Exception {
		long start = System.currentTimeMillis();
		//无返回值
		ForkJoinPool fjp = new ForkJoinPool();
//		AddTask task = new AddTask(0, nums.length);
//		fjp.execute(task);
//		task.join();
//		fjp.awaitTermination(2, TimeUnit.SECONDS);
//		fjp.submit(task);
		
		//有返回值
		AddTaskRes task1 = new AddTaskRes(0, nums.length);
		Long sum = fjp.invoke(task1);
		System.out.println(sum);
//		fjp.execute(task1);
//		task1.join();
//		fjp.awaitTermination(2, TimeUnit.SECONDS);
		long end = System.currentTimeMillis();
		System.out.println("耗时:" + (end -start));
		fjp.shutdown();	
	}

好了,今天的分享就到这里了,对文章感兴趣的关注下博主吧~~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值