Sike hands write java thread pool thread a series of their own

I welcome the attention of the public number "Tong brother read source" view source code more series, and brother Tong source of ocean swim together.

mythreadpool

(Horizontal screen phone to see the source code is more convenient)


problem

(1) write himself a thread pool What factors need to be considered?

(2) to write their own thread pool how to test?

Brief introduction

Java thread pool is often used to concurrent programming techniques, how to write himself a thread pool it? This article will be hands-on with your brother Tong wrote an available thread pool.

Attribute analysis

Thread pool, as the name suggests it is above all a "pool", which put the pool is a thread, the thread is used to perform the task.

First , the threads in the thread pool should be a category, some kernel threads, and some non-core thread, so we need two variables to identify the number of cores and threads coreSize maximum number of threads maxSize.

Why should distinguish whether the kernel thread it? This is to control the number of threads in the system.

When the number of threads in the thread pool does not reach the core number of threads coreSize time, add a thread to a task is possible, you can also improve the efficiency of task execution.

When the number of threads in the thread pool to reach the core number of threads, the number of threads have to control what, advanced to the task queue, if the task is executed fast enough, these core thread will soon be able to complete the task execution queue, no new the necessary thread.

When the task queue is full, and this time the core thread alone can not handle the task in a timely manner, so this time you need to add a new thread, but the thread can not be increased indefinitely, so it is necessary to control the maximum number of threads maxSize.

Secondly , we need to hold a task queue task, the queue must be thread-safe, we generally use BlockingQueue to act as a blocking queue, of course, also possible to use ConcurrentLinkedQueue (note ConcurrentLinkedQueue not blocking queue, can not be used in jdk thread pool) .

Finally , as more and more tasks threading is not timely, will sooner or later reach a state queue is full, the number of threads is also the maximum number of threads, and this time how to do it? One strategy this time you need to go deny policy, that is how these tasks can not be processed in time to do, there are common strategy discard the current task, discard the oldest tasks, deal with their own caller, an exception is thrown and so on.

According to the above description , we need to define a thread pool so a total of four variables: the number of kernel threads coreSize, the maximum number of threads maxSize, blocking queue BlockingQueue, deny policy RejectPolicy.

In addition, in order to facilitate a name to the thread pool, we add a variable: the name of the thread pool name.

So we come to the thread pool properties and method of construction is probably as follows:

public class MyThreadPoolExecutor implements Executor {
    /**
     * 线程池的名称
     */
    private String name;
    /**
     * 核心线程数
     */
    private int coreSize;
    /**
     * 最大线程数
     */
    private int maxSize;
    /**
     * 任务队列
     */
    private BlockingQueue<Runnable> taskQueue;
    /**
     * 拒绝策略
     */
    private RejectPolicy rejectPolicy;

    public MyThreadPoolExecutor(String name, int coreSize, int maxSize, BlockingQueue<Runnable> taskQueue, RejectPolicy rejectPolicy) {
        this.name = name;
        this.coreSize = coreSize;
        this.maxSize = maxSize;
        this.taskQueue = taskQueue;
        this.rejectPolicy = rejectPolicy;
    }
}

Task flow analysis

According to attribute the above analysis, basically we've got the complete logical flow of tasks:

First , if the number of threads running less than the core number of threads, directly create a new core thread to run a new task.

Secondly , if the number of threads running to reach the core number of threads, put the new task into the queue.

Then , if the queue is also full, then create a new thread to run the new non-core tasks.

Finally , if the number of non-core threads have reached the maximum, and it refused to execute strategy.

mythreadpool

Code logic is as follows:

    @Override
    public void execute(Runnable task) {
        // 正在运行的线程数
        int count = runningCount.get();
        // 如果正在运行的线程数小于核心线程数,直接加一个线程
        if (count < coreSize) {
            // 注意,这里不一定添加成功,addWorker()方法里面还要判断一次是不是确实小
            if (addWorker(task, true)) {
                return;
            }
            // 如果添加核心线程失败,进入下面的逻辑
        }

        // 如果达到了核心线程数,先尝试让任务入队
        // 这里之所以使用offer(),是因为如果队列满了offer()会立即返回false
        if (taskQueue.offer(task)) {
            // do nothing,为了逻辑清晰这里留个空if
            // 【本篇文章由公众号“彤哥读源码”原创】
        } else {
            // 如果入队失败,说明队列满了,那就添加一个非核心线程
            if (!addWorker(task, false)) {
                // 如果添加非核心线程失败了,那就执行拒绝策略
                rejectPolicy.reject(task, this);
            }
        }
    }

Create a logical analysis thread

First , based on the number of threads to create a thread is running has not reached the core number of threads or the maximum number of threads, so we also need to record the number of threads that are running a variable runningCount used.

Secondly , this variable runningCount needs simple calculations in a concurrent environment, so there need to use the CAS Unsafe instructions to control its value modified by the CAS will add to this variable volatile modified, for convenience here we used directly AtomicInteger as the type of the variable.

Then , because it is complicated by the environment, it is necessary to determine runningCount <coreSize (or the maxSize) (condition a) is modified while runningCount CAS plus a (second condition) is successful only represents an additional thread, if the conditions for a failure indicates no longer increased direct thread returns false, if the condition fails, it said two other threads to modify the value runningCount, then retry.

Finally , create a thread and run a new task, and continue to take the job from the queue to run [This article from the original public number "Tong brother read source"].

mythreadpool

Code logic is as follows:

    private boolean addWorker(Runnable newTask, boolean core) {
        // 自旋判断是不是真的可以创建一个线程
        for (; ; ) {
            // 正在运行的线程数
            int count = runningCount.get();
            // 核心线程还是非核心线程
            int max = core ? coreSize : maxSize;
            // 不满足创建线程的条件,直接返回false
            if (count >= max) {
                return false;
            }
            // 修改runningCount成功,可以创建线程
            if (runningCount.compareAndSet(count, count + 1)) {
                // 线程的名字
                String threadName = (core ? "core_" : "") + name + sequence.incrementAndGet();
                // 创建线程并启动
                new Thread(() -> {
                    System.out.println("thread name: " + Thread.currentThread().getName());
                    // 运行的任务
                    Runnable task = newTask;
                    // 不断从任务队列中取任务执行,如果取出来的任务为null,则跳出循环,线程也就结束了
                    while (task != null || (task = getTask()) != null) {
                        try {
                            // 执行任务
                            task.run();
                        } finally {
                            // 任务执行完成,置为空
                            task = null;
                        }
                    }
                }, threadName).start();

                break;
            }
        }

        return true;
    }

Take the task of logical analysis

From the queue to take the task should use the take () method, which would have been blocked until taken to task or interrupt, if the interrupt returns null, so the current thread it will be quietly ended Also note interrupted remember to runningCount minus one.

    private Runnable getTask() {
        try {
            // take()方法会一直阻塞直到取到任务为止
            return taskQueue.take();
        } catch (InterruptedException e) {
            // 线程中断了,返回null可以结束当前线程
            // 当前线程都要结束了,理应要把runningCount的数量减一
            runningCount.decrementAndGet();
            return null;
        }
    }

Well, here we own thread pool to finish, let's work together to think about how to test it?

Test logic analysis

We look back at their construction methods written thread pool:

    public MyThreadPoolExecutor(String name, int coreSize, int maxSize, BlockingQueue<Runnable> taskQueue, RejectPolicy rejectPolicy) {
        this.name = name;
        this.coreSize = coreSize;
        this.maxSize = maxSize;
        this.taskQueue = taskQueue;
        this.rejectPolicy = rejectPolicy;
    }

name, this casually pass;

coreSize, we assume 5;

maxSize, we assumed to be 10;

taskQueue, task queue, since it is a boundary we set, we use the most simple ArrayBlockingQueue Well, size is set to 15, so that the inside can store up to 15 tasks;

rejectPolicy, refused strategy, we assumed discard the current task of strategy, OK, we have to achieve a.

/**
 * 丢弃当前任务
 */
public class DiscardRejectPolicy implements RejectPolicy {
    @Override
    public void reject(Runnable task, MyThreadPoolExecutor myThreadPoolExecutor) {
        // do nothing
        System.out.println("discard one task");
    }
}

OK, so we created a thread pool, the following is to perform the task, we assume that add good 100 tasks for loop continuously.

public class MyThreadPoolExecutorTest {
    public static void main(String[] args) {
        Executor threadPool = new MyThreadPoolExecutor("test", 5, 10, new ArrayBlockingQueue<>(15), new DiscardRejectPolicy());
        AtomicInteger num = new AtomicInteger(0);

        for (int i = 0; i < 100; i++) {
            threadPool.execute(()->{
                try {
                    Thread.sleep(1000);
                    System.out.println("running: " + System.currentTimeMillis() + ": " + num.incrementAndGet());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }
}

We analyzed under this program:

(1) to create a continuous five core threads, and perform a new task;

Task 15 (2) back into the queue;

(3) the queue is full, creating a succession of five threads, and perform a new task;

(4) no later mission was carried out, all gone discard policy;

(5) So the real successful implementation of the task should be 15 + 5 + 5 = 25 tasks;

Run it:

thread name: core_test2
thread name: core_test5
thread name: core_test3
thread name: core_test4
thread name: core_test1
thread name: test6
thread name: test7
thread name: test8
thread name: test9
discard one task
thread name: test10
discard one task
...省略被拒绝的任务
【本篇文章由公众号“彤哥读源码”原创】
discard one task
running: 1570546871851: 2
running: 1570546871851: 8
running: 1570546871851: 7
running: 1570546871851: 6
running: 1570546871851: 5
running: 1570546871851: 3
running: 1570546871851: 4
running: 1570546871851: 1
running: 1570546871851: 10
running: 1570546871851: 9
running: 1570546872852: 14
running: 1570546872852: 20
running: 1570546872852: 19
running: 1570546872852: 17
running: 1570546872852: 18
running: 1570546872852: 16
running: 1570546872852: 15
running: 1570546872852: 12
running: 1570546872852: 13
running: 1570546872852: 11
running: 1570546873852: 21
running: 1570546873852: 24
running: 1570546873852: 23
running: 1570546873852: 25
running: 1570546873852: 22

We can see, creating five core threads, five non-core threads, the successful implementation of the 25 tasks to complete no problem, perfect ^^.

to sum up

(1) write himself a thread pool factors to be considered are: a core number of threads, the maximum number of threads, task queue, deny policy.

(2) creates a thread to be vigilant when concurrent trap;

Egg

We know, jdk own thread pool there are two parameters: keepAliveTime, unit, they are doing it?

A: They are used to control when the destruction of non-core thread, of course, can destroy the core thread, please look forward to the next chapter a detailed analysis of it.

Complete source code

Executor interface

public interface Executor {
    void execute(Runnable command);
}

MyThreadPoolExecutor thread pool implementation class

/**
 * 自动动手写一个线程池
 */
public class MyThreadPoolExecutor implements Executor {

    /**
     * 线程池的名称
     */
    private String name;
    /**
     * 线程序列号
     */
    private AtomicInteger sequence = new AtomicInteger(0);
    /**
     * 核心线程数
     */
    private int coreSize;
    /**
     * 最大线程数
     */
    private int maxSize;
    /**
     * 任务队列
     */
    private BlockingQueue<Runnable> taskQueue;
    /**
     * 拒绝策略
     */
    private RejectPolicy rejectPolicy;
    /**
     * 当前正在运行的线程数【本篇文章由公众号“彤哥读源码”原创】
     * 需要修改时线程间立即感知,所以使用AtomicInteger
     * 或者也可以使用volatile并结合Unsafe做CAS操作(参考Unsafe篇章讲解)
     */
    private AtomicInteger runningCount = new AtomicInteger(0);

    public MyThreadPoolExecutor(String name, int coreSize, int maxSize, BlockingQueue<Runnable> taskQueue, RejectPolicy rejectPolicy) {
        this.name = name;
        this.coreSize = coreSize;
        this.maxSize = maxSize;
        this.taskQueue = taskQueue;
        this.rejectPolicy = rejectPolicy;
    }

    @Override
    public void execute(Runnable task) {
        // 正在运行的线程数
        int count = runningCount.get();
        // 如果正在运行的线程数小于核心线程数,直接加一个线程
        if (count < coreSize) {
            // 注意,这里不一定添加成功,addWorker()方法里面还要判断一次是不是确实小
            if (addWorker(task, true)) {
                return;
            }
            // 如果添加核心线程失败,进入下面的逻辑
        }

        // 如果达到了核心线程数,先尝试让任务入队
        // 这里之所以使用offer(),是因为如果队列满了offer()会立即返回false
        if (taskQueue.offer(task)) {
            // do nothing,为了逻辑清晰这里留个空if
        } else {
            // 如果入队失败,说明队列满了,那就添加一个非核心线程
            if (!addWorker(task, false)) {
                // 如果添加非核心线程失败了,那就执行拒绝策略
                rejectPolicy.reject(task, this);
            }
        }
    }

    private boolean addWorker(Runnable newTask, boolean core) {
        // 自旋判断是不是真的可以创建一个线程
        for (; ; ) {
            // 正在运行的线程数
            int count = runningCount.get();
            // 核心线程还是非核心线程
            int max = core ? coreSize : maxSize;
            // 不满足创建线程的条件,直接返回false
            if (count >= max) {
                return false;
            }
            // 修改runningCount成功,可以创建线程
            if (runningCount.compareAndSet(count, count + 1)) {
                // 线程的名字
                String threadName = (core ? "core_" : "") + name + sequence.incrementAndGet();
                // 创建线程并启动
                new Thread(() -> {
                    System.out.println("thread name: " + Thread.currentThread().getName());
                    // 运行的任务【本篇文章由公众号“彤哥读源码”原创】
                    Runnable task = newTask;
                    // 不断从任务队列中取任务执行,如果取出来的任务为null,则跳出循环,线程也就结束了
                    while (task != null || (task = getTask()) != null) {
                        try {
                            // 执行任务
                            task.run();
                        } finally {
                            // 任务执行完成,置为空
                            task = null;
                        }
                    }
                }, threadName).start();

                break;
            }
        }

        return true;
    }

    private Runnable getTask() {
        try {
            // take()方法会一直阻塞直到取到任务为止
            return taskQueue.take();
        } catch (InterruptedException e) {
            // 线程中断了,返回null可以结束当前线程
            // 当前线程都要结束了,理应要把runningCount的数量减一
            runningCount.decrementAndGet();
            return null;
        }
    }

}

RejectPolicy reject strategy interfaces

public interface RejectPolicy {
    void reject(Runnable task, MyThreadPoolExecutor myThreadPoolExecutor);
}

DiscardRejectPolicy discard policy implementation class

/**
 * 丢弃当前任务
 */
public class DiscardRejectPolicy implements RejectPolicy {
    @Override
    public void reject(Runnable task, MyThreadPoolExecutor myThreadPoolExecutor) {
        // do nothing
        System.out.println("discard one task");
    }
}

Test category

public class MyThreadPoolExecutorTest {
    public static void main(String[] args) {
        Executor threadPool = new MyThreadPoolExecutor("test", 5, 10, new ArrayBlockingQueue<>(15), new DiscardRejectPolicy());
        AtomicInteger num = new AtomicInteger(0);

        for (int i = 0; i < 100; i++) {
            threadPool.execute(()->{
                try {
                    Thread.sleep(1000);
                    System.out.println("running: " + System.currentTimeMillis() + ": " + num.incrementAndGet());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

    }
}

I welcome the attention of the public number "Tong brother read source" view source code more series, and brother Tong source of ocean swim together.

qrcode

Guess you like

Origin www.cnblogs.com/tong-yuan/p/11639269.html