Java multi-threading - the source code used to create a thread pool to expand

Multithreaded design method can indeed maximize the computing power of multi-core processors, improving throughput and performance. But if uncontrolled free to use threads, but the performance of the system may result in adverse.

And processes in comparison, although the thread is a lightweight, but still closed to create and takes time, if every small task to create a thread, you will most likely occur creating and destroying threads occupy more time than the threaded task consumes time. Secondly thread itself also needs to take up memory space, a large number of threads to seize the precious memory resources.

Therefore, the use of threads need to have a degree, increasing the number of threads within a limited range can improve the performance of the system, once more than this range, a large number of threads will only drag down the entire system.

1 What is the thread pool

In order to avoid frequent system of creating and destroying threads, we can create a thread to reuse. We can use a thread pool to maintain some threads, when you need to use threads, you can simply take a free thread from the pool, when the work is completed, in no hurry to close the thread, but these will be returned to the thread pool thread to facilitate the next use.

In short, after the re-use thread pool threads to create a program to get free from the thread pool, close the thread pool thread becomes wanted to return the thread.

2 Create a thread pool

Members of the thread pool in java.util.concurrent package, is the core JDK and contract. ThreadPoolExecutor which represents a thread pool. Executors class is the role of a thread factory, you can get a thread pool has a specific function by Executors, you can get a specific function of the thread pool by Executors.

2.1 newFixedThreadPool()方法

This method returns a fixed number of threads of thread pool. The number of threads in the thread pool is always the same. When there is a new task submission, if the thread pool idle threads is executed immediately. If not, the new task will be temporarily stored in a task queue, when there is a thread to be idle, they handle the task queue queue.

2.2 newSingleThreadExecutor () 方法

This method returns only one thread in a thread pool. If the excess is submitted to the thread pool task, the task will be saved in a task queue until the thread is idle, to perform the task queue of first-in first-out order.

2.3 newCachedThreadPool()方法

This method returns a number of threads can be adjusted according to the actual thread pool. The number of threads in the thread pool is uncertain, but can be reused if idle threads will use the thread priority reusable. If all the threads are at work, another new task submission, new thread processing task is created. All threads in the current task is finished, it will return to the thread pool multiplex.

2.4 newSingleThreadScheduledExecutor () 方法

This method returns a ScheduledExecutorService objects, thread pool size is 1. ScheduledExecutorService interfaces on ExecutorService interface extends the functionality in the execution of a task at a given time, such as after a fixed delay in execution, or periodically perform a task.

2.5 newScheduledThreadPool()方法

This method returns a ScheduledExecutorService object, but the number of threads that can execute thread pool.

Create a fixed-size thread pool

public class ThreadPoolThread {

    public static class MyTask implements Runnable{

        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + ":Thread ID: " + Thread.currentThread().getId());
            try{
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public static void main(String[] args) {
            MyTask myTask = new MyTask();
            ExecutorService executorService = Executors.newFixedThreadPool(5);
            for (int i = 0;i< 10 ;i++){
                executorService.submit(myTask);
            }
        }
    }

}
复制代码
1562554721820:Thread ID: 12
1562554721820:Thread ID: 15
1562554721820:Thread ID: 16
1562554721820:Thread ID: 13
1562554721820:Thread ID: 14
1562554722821:Thread ID: 15
1562554722821:Thread ID: 16
1562554722821:Thread ID: 12
1562554722821:Thread ID: 13
1562554722821:Thread ID: 14
复制代码

Planned mission

newScheduledThreadPool () method returns a ScheduledExecutorService objects, thread scheduling may be required depending on the time. The main methods are as follows

public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,         long period,TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);
复制代码

Unlike other threads, ScheduledExecutorService necessarily will arrange to perform tasks. He actually played a role in the scheduled task, the task will be scheduled for a specified time.

schedule () will conduct a scheduled task at a given time. scheduleAtFixedRate () and scheduleWithFixedDelay () method will be periodic task scheduling, but there are two differences. Scheduling frequency scheduleAtFixedRate () method is constant, it is time to begin execution of a task or more as a starting point, and then a mission at a predetermined time schedule. The scheduleWithFixedDelay () method is scheduling a predetermined time elapses after the end of more than one task.

public class ScheduleExecutorServiceDemo {

    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    System.out.println(System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },0,2, TimeUnit.SECONDS);
    }
}
复制代码
1562555518798
1562555520798
1562555522798
1562555524799
1562555526800
复制代码

It can be seen once the task is scheduled every two seconds.

If the execution time of the task is greater than the scheduled time, the task will be called immediately after the end of the task.

The code modification to 8 seconds

Thread.sleep(8000);
复制代码
1562555680333
1562555688333
1562555696333
1562555704333
复制代码

Scheduler does not actually guarantee continued indefinitely call the task, if the task itself throws an exception, then all subsequent execution will be interrupted.

3 internal thread pool implementation

For a few core thread pool, although looking to create a pool of threads have different features, but its interior is used ThreadPoolExecutor class.

Look at the source code to create a pool of several threads:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
    }
    
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
    }
复制代码

It can be seen that they are ThreadPoolExecutor class package, look at the structure ThreadPoolExecutor class:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
复制代码
  • corePoolSize: specifies the number of threads in the pool.
  • maximumPoolSize: specifies the maximum number of threads in the thread pool.
  • keepAliveTime: When the number of threads in the pool than corePoolSize, excess idle threads of survival time, or more than corePoolSize idle thread is destroyed how long.
  • unit: the unit keepAliveTime.
  • workQueue: task queue, the queue storing tasks are submitted but has not yet been executed.
  • threadFacotry: thread factory used to create a thread.
  • handler: rejection policy. When the task is too late to deal with how to refuse the task.

3.1 workQueue- task queue

WorkQueue parameter refers to the task queue is submitted but not executed, he is the object of a BlockingQueue interface Runnable only for storing objects. In ThreadPoolExecutor construction can be used at several BlockingQueue Interface:

  • Direct submission queue: There SynchronousQueue object provides. SynchronousQueue no capacity, each insert operation must wait a corresponding deletion operation, whereas each corresponding to a delete operation must wait insertion operation. Use SynchronousQueue if there are always new tasks presented to the thread execution, if not idle process tries to create a new thread if the thread has reached the maximum number is executed deny policy. Use SynchronousQueue maximumPoolSize usually set great value, it will be very easy to perform denial strategy.
  • Bounded task queue: queues have bounded task implemented using ArrayBlockingQueue class constructor class must ArrayBlockingQueue with a capacity parameter that indicates the maximum capacity of the queue. When using bounded task queue, if new tasks to perform, when the actual threads in the thread pool is less than corePoolSize priority is to create threads, if more than corePoolSize task will be added to the waiting queue. If the queue is full, creating a new thread to perform tasks in case of a total of not more than maximumPoolSize thread, if greater than maximumPoolSize execute deny policy.
  • Unbounded queue of tasks: Task unbounded queue using LinkedBlockingQueue class implementation. Compared with bounded task queue, the task queue unbounded case the team lost not appear. Use LinkedBlockingQueue when new tasks need to thread execution, if the number is less than corePoolSize thread, a new thread is created, but the number of threads reaches corePoolSize will not continue to grow after the. Follow-up if there is no new task is added directly into the queue of idle threads waiting.
  • Queue priorities: priority queue with a queue execution priority. By PriorityBlockingQueue class implementation, you can control the execution order of tasks. It is a special unbounded queue. PriorityBlockingQueue class can be performed according to the priority order of the task itself.

4 denial policy

Refused strategy is executed when the policy exceeds the carrying capacity of the system actual number of tasks. Refused strategy can be said that the remedies system overload operation.

JDK built four refused strategy:

  • AbortPolicy Strategy: The strategy will direct throw an exception that prevents normal operation.
  • CallerRunsPolicy strategy: as long as the thread pool is not closed, the policy runs directly current task is discarded in the current caller thread. This does not really dropped threads, but will make the job submission thread performance.
  • DiscardOldestPolicy strategy: This strategy will discard the oldest one request, that is a task to be executed, and try to submit the current job again.
  • DiscardPolicy Policy: This policy drops task can not be processed without any treatment.

The above strategies are implemented RejectedExecutionHandler interfaces, if the above policy can not meet the actual development, you can extend yourself.

RejectedExecutionHandler interface constructs:

public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
复制代码

Custom refused strategy:

//拒绝策略demo
public class RejectThreadPoolDemo {

    public static class MyTask implements Runnable{

        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + ": Thread ID : " + Thread.currentThread().getId());
            try{
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyTask myTask = new MyTask();
        ThreadPoolExecutor es = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(10), Executors.privilegedThreadFactory(), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println(r.toString() + "被拒绝");
            }
        });
        for (int i = 0;i<Integer.MAX_VALUE;i++){
           es.submit(myTask);
           Thread.sleep(10);
        }
    }

}
复制代码
1562575292467: Thread ID : 14
1562575292478: Thread ID : 15
1562575292489: Thread ID : 16
java.util.concurrent.FutureTask@b4c966a被拒绝
java.util.concurrent.FutureTask@2f4d3709被拒绝
java.util.concurrent.FutureTask@4e50df2e被拒绝
复制代码

5 custom thread creation: ThreadFactory

ThreadFactory is an interface, he is only one way to create a thread.

Thread newThread(Runnable r);
复制代码

We can create a thread pool to track when and how many threads to create a custom thread name and other custom threads.

public class ThreadFactoryDemo {

    static volatile int i = 0;

    public static class TestTask implements Runnable{

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        TestTask testTask = new TestTask();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<>(), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r,"test--" + i);
                i++;
                return thread;
            }
        });
        for (int i = 0;i<5;i++){
            threadPoolExecutor.submit(testTask);
        }
    }

}
复制代码
test--0
test--1
test--4
test--2
test--3
复制代码

6 extended thread pool

Although JDK has helped us to achieve a stable thread pool, but if we want to extend some of the thread pool, how do the start and end time to perform tasks such as monitoring it.

ThreadPoolExecutor is a scalable thread pool, which provides beforExecutor (), afterExecutor () and terminated () three interfaces to be extended.

public class ThreadFactoryDemo {

    static volatile int i = 0;

    public static class TestTask implements Runnable{

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }

    public static void main(String[] args) {
        TestTask testTask = new TestTask();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<>(), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r,"test--" + i);
                i++;
                return thread;
            }
        }){
            @Override
            protected void beforeExecute(Thread t,Runnable r){
                System.out.println("task-----准备执行");
            }
        };
        for (int i = 0;i<5;i++){
            threadPoolExecutor.submit(testTask);
        }
    }

}
复制代码
task-----准备执行
task-----准备执行
test--2
task-----准备执行
test--1
task-----准备执行
test--4
task-----准备执行
test--3
test--0
复制代码

7 submit and execute the difference

7.1 execute () method

Submit submit only way to execute a Runnable objects, and the return value is void, that is, if submitted after the thread runs, and the main thread out of the relationship, of course, you can set some variables to get to the thread operation result. And when the execution thread exception is thrown in general, the main thread can not get the information abnormalities, only the active set of threads by ThreadFactory Exception class can be perceived anomalies in the thread submit.

7.2 sumbit () method

Sumbit () method of three forms:

public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
复制代码

sumbit method returns a Future object, the Future object that represents the results of this thread, when the main thread calls the get method of the Future when it will get to the result data returned from the thread. If an exception occurs during the execution of the thread in, get the information would get the exception.

Guess you like

Origin juejin.im/post/5d23083ff265da1b971a9823