Java concurrent queue and thread pool

Concurrent queue

  In the concurrent queue JDKprovides two sets to achieve, is a ConcurrentLinkedQueuehigh-performance non-blocking queue queue on behalf of, a is BlockingQueuethe interface represented by blocking queue, no matter what are inherited from Queue.

Blocking and non-blocking queue team

  Blocking queue with the difference that ordinary queue, when the queue is empty , to obtain elements from the queue operation will be blocked , or when the queue is full , add to the queue operating elements will be blocked . Trying to empty the blocking queue to obtain a thread elements will be blocked until the other thread to empty the queue insert new elements. Also, try to go full blocking queue to add a new element of the same thread will be blocked until the other threads queue up again become idle, such as removing one or more elements from the queue, or to completely empty the queue.

1.ArrayDeque, (数组双端队列) 
2.PriorityQueue, (优先级队列) 
3.ConcurrentLinkedQueue, (基于链表的并发队列) 
4.DelayQueue, (延期阻塞队列)(阻塞队列实现了BlockingQueue接口) 
5.ArrayBlockingQueue, (基于数组的并发阻塞队列) 
6.LinkedBlockingQueue, (基于链表的FIFO阻塞队列) 
7.LinkedBlockingDeque, (基于链表的FIFO双端阻塞队列) 
8.PriorityBlockingQueue, (带优先级的无界阻塞队列) 
9.SynchronousQueue (并发同步阻塞队列)
......

ConcurrentLinkedQueue

ConcurrentLinkedQueue: Is applicable to a queue at high concurrency scenarios, by lock-free way to achieve high performance under high concurrency state, usually ConcurrentLinkedQueueperformance is better than BlockingQueueit is an unbounded thread-safe queue based on linked nodes. Elements of the queue to follow the FIFO principle. Is added to the first head, the tail is recently added to the queue does not allow nullelement.
ConcurrentLinkedQueueImportant ways:
addand offer()methods are added to the elements (in ConcurrentLinkedQueueno difference in maybe has any method)
poll()and peek()is to take the first element node, except that it removes the element, not the latter.

import java.util.concurrent.ConcurrentLinkedQueue;

public class Q001_ConcurrentLinkedQueueTest {
    public static void main(String[] args) {
        ConcurrentLinkedQueue<String> concurrentLinkedQueue = new ConcurrentLinkedQueue<String>();
        concurrentLinkedQueue.offer("aaa");
        concurrentLinkedQueue.offer("bbb");
        //从队列头获取元素,此方法没有将元素移出队列(peek--->偷看;看一眼;一瞥; )
        System.out.println(concurrentLinkedQueue.peek());
        System.out.println(concurrentLinkedQueue.size());
        //从队列头获取元素,并将其移出队列
        System.out.println(concurrentLinkedQueue.poll());
        System.out.println(concurrentLinkedQueue.size());
        //继续获取
        System.out.println(concurrentLinkedQueue.poll());
        //此时队列总没有元素了 输出为null
        System.out.println(concurrentLinkedQueue.poll());
    }
}

Results of the

aaa
2
aaa
1
bbb
null

BlockingQueue

Blocking queue ( BlockingQueue) is a queue support two additional operations. These two additional operations are:

  1. In the queue is empty when retrieving an element of thread will wait for the queue to become non-empty.
  2. When the queue is full, the thread storage elements will wait queue available.

  Therefore, when a thread attempts to have a full queue of queues into operation, it will be blocked, unless there is another thread to do the queue operation; Similarly, when a thread tries to empty the queue queue operation , it will be blocked, unless there is another thread into the queue operation.
  In Java, the BlockingQueueinterface is located in java.util.concurrentthe package (in Java5began offering versions), by blocking queue characteristics described above it shows that blocking queue is thread-safe of.
  In the new Concurrentpackage, BlockingQueuea good solution to multiple threads, how efficient and safe "transfer" of data problems. These efficient and thread-safe queue class, a great convenience for us to quickly build high-quality multi-threaded programs.
  Blocking queue commonly used in the producers and consumers of the scene , the producer is added to the queue thread elements, the consumer is to take elements from the queue thread. Blocking queue storage container element is the producers, and consumers only take elements from the container.

Figure queue
Figure queue

  

ArrayBlockingQueue

ArrayBlockingQueueThere is a blocking queue borders, its internal implementation is an array. There is meaning boundary its capacity is limited, we have to specify the size of its capacity at the time of its initialization, the capacity of the specified size once it is immutable.
ArrayBlockingQueueFIFO data is stored, the newly inserted object is the tail, the latest object is removed from the head.
The following is an initialize and use ArrayBlockingQueuean example:


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;


/**
 * 阻塞队列
 * 在队列满的时候,存储元素的线程会等待队列可用
 * 队列为空时, 读取元素的线程 会等待队列变为非空
 * 
 * @author hao
 *
 */
public class Q002_ArrayBlockingQueue {
    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        
        arrayBlockingQueue.offer("小明");
        arrayBlockingQueue.offer("小李");
        arrayBlockingQueue.offer("笑话", 3, TimeUnit.SECONDS);
        System.out.println("插入第四条开始");
        System.out.println(arrayBlockingQueue.offer("校长", 3, TimeUnit.SECONDS));
        System.out.println("插入第四条结束");
        //前三个元素一次出列
        System.out.println(arrayBlockingQueue.poll(3,TimeUnit.SECONDS));
        System.out.println(arrayBlockingQueue.poll(3,TimeUnit.SECONDS));
        System.out.println(arrayBlockingQueue.poll(3,TimeUnit.SECONDS));
        System.out.println("此时队列中元素"+arrayBlockingQueue.size());
        //第四个元素出列(其实第四个元素没有入列)
        System.out.println(arrayBlockingQueue.poll(3,TimeUnit.SECONDS));
        System.out.println("此时队列中元素"+arrayBlockingQueue.size());
    }
}

Results of the

插入第四条开始
false       # 这里延时了三秒
插入第四条结束
小明
小李
笑话
此时队列中元素0 
null         #这里延时了三秒
此时队列中元素0

LinkedBlockingQueue

LinkedBlockingQueue阻塞队列大小的配置是可选的,如果我们初始化时指定一个大小,它就是有边界的,如果不指定,它就是无边界的。说是无边界,其实是采用了默认大小为Integer.MAX_VALUE的容量 。
它的内部实现是一个链表。和ArrayBlockingQueue一样,LinkedBlockingQueue 也是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。下面是一个初始化和使LinkedBlockingQueue的例子:

import java.util.concurrent.LinkedBlockingQueue;
public class Q003_LinkedBlockingQueue {
    public static void main(String[] args) {
        LinkedBlockingQueue<String> linkedBlockingQueue = new LinkedBlockingQueue<String>(2);
        System.out.println(linkedBlockingQueue.offer("bbb"));
        System.out.println(linkedBlockingQueue.offer("aaa"));
        System.out.println(linkedBlockingQueue.offer("ccc")); //容量满的时候返回false
        //add 和 offer 都可以添加队列 
        System.out.println(linkedBlockingQueue.add("ddd")); //容量满是抛出异常
    }
}

执行结果

true
true
false
Exception in thread "main" java.lang.IllegalStateException: Queue full
    at java.util.AbstractQueue.add(AbstractQueue.java:98)
    at com.hao.queue.Q003_LinkedBlockingQueue.main(Q003_LinkedBlockingQueue.java:12)

PriorityBlockingQueue

  PriorityBlockingQueue是一个没有边界的队列,它的排序规则和 java.util.PriorityQueue一样。需要注意,PriorityBlockingQueue中允许插入null对象。所有插入PriorityBlockingQueue的对象必须实现java.lang.Comparable接口,队列优先级的排序规则就是按照我们对这个接口的实现来定义的。另外,我们可以从PriorityBlockingQueue获得一个迭代器Iterator,但这个迭代器并不保证按照优先级顺 序进行迭代。

SynchronousQueue

SynchronousQueue队列内部仅允许容纳一个元素。当一个线程插入一个元素后会被阻塞,除非这个元素被另一个线程消费。

使用BlockingQueue模拟生产者与消费者

生产者

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ProducerThread implements Runnable {
    private BlockingQueue<String> blockingQueue;
    private AtomicInteger count = new AtomicInteger();
    private volatile boolean FLAG = true;
    public ProducerThread(BlockingQueue<String> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "生产者开始启动....");
        while (FLAG) {
            String data = count.incrementAndGet() + "";
            try {
                boolean offer = blockingQueue.offer(data, 2, TimeUnit.SECONDS);
                if (offer) {
                    System.out.println(Thread.currentThread().getName() + ",生产队列" + data + "成功..");
                } else {
                    System.out.println(Thread.currentThread().getName() + ",生产队列" + data + "失败..");
                }
                Thread.sleep(1000);
            } catch (Exception e) {

            }
        }
        System.out.println(Thread.currentThread().getName() + ",生产者线程停止...");
    }
    public void stop() {
        this.FLAG = false;
    }
}

消费者

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class ConsumerThread implements Runnable {
    private volatile boolean FLAG = true;
    private BlockingQueue<String> blockingQueue;
    public ConsumerThread(BlockingQueue<String> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "消费者开始启动....");
        while (FLAG) {
            try {
                String data = blockingQueue.poll(2, TimeUnit.SECONDS);
                if (data == null || data == "") {
                    FLAG = false;
                    System.out.println("消费者超过2秒时间未获取到消息.");
                    return;
                }
                System.out.println("消费者获取到队列信息成功,data:" + data);

            } catch (Exception e) {}
        }
    }
}

测试代码

import com.hao.queuepc.ConsumerThread;
import com.hao.queuepc.ProducerThread;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class Test0005 {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<String>();
        ProducerThread producer = new ProducerThread(blockingQueue);
        ConsumerThread consumer = new ConsumerThread(blockingQueue);
        
        Thread p = new Thread(producer);
        Thread c = new Thread(consumer);
        p.start();
        c.start();
        
        try {
            //10秒后停止生产者线程
            Thread.sleep(10*1000);
            producer.stop();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }   
                
    }
}

执行结果

Thread-0生产者开始启动....
Thread-1消费者开始启动....
Thread-0,生产队列1成功..
消费者获取到队列信息成功,data:1
Thread-0,生产队列2成功..
消费者获取到队列信息成功,data:2
Thread-0,生产队列3成功..
消费者获取到队列信息成功,data:3
Thread-0,生产队列4成功..
消费者获取到队列信息成功,data:4
Thread-0,生产队列5成功..
消费者获取到队列信息成功,data:5
Thread-0,生产队列6成功..
消费者获取到队列信息成功,data:6
Thread-0,生产队列7成功..
消费者获取到队列信息成功,data:7
Thread-0,生产队列8成功..
消费者获取到队列信息成功,data:8
Thread-0,生产队列9成功..
消费者获取到队列信息成功,data:9
Thread-0,生产队列10成功..
消费者获取到队列信息成功,data:10
Thread-0,生产者线程停止...
消费者超过2秒时间未获取到消息.

线程池

什么是线程池

  Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用线程池,必须对其实现原理了如指掌。

线程池作用

  线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。
  如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且我们还不能控制线程池中线程的开始、挂起、和中止。

线程池四种创建方式

Java通过Executorsjdk1.5并发包)提供四种线程池,分别为:

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

newCachedThreadPool

  创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class P001_Cached {
    public static void main(String[] args) {
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int temp = i;
            newCachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
//                  try {
//                      Thread.sleep(2000);
//                  } catch (InterruptedException e) {
//                      e.printStackTrace();
//                  }
                    System.out.println(Thread.currentThread().getName() + ",i" + temp);
                }
            });
        }
    }
}

执行结果

pool-1-thread-2,i1
pool-1-thread-4,i3
pool-1-thread-3,i2
pool-1-thread-1,i0
pool-1-thread-5,i4
pool-1-thread-1,i5
pool-1-thread-4,i7
pool-1-thread-5,i6
pool-1-thread-3,i8
pool-1-thread-5,i9

总结: 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

newFixedThreadPool

  创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class P002_Fixed {
    public static void main(String[] args) {
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; i++) {
            final int temp = i;
            newFixedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "," + temp);
                }
            });
        }
    }

}

执行结果

pool-1-thread-1,0
pool-1-thread-2,1
pool-1-thread-3,2
pool-1-thread-1,3
pool-1-thread-2,5
pool-1-thread-3,4
pool-1-thread-2,7
pool-1-thread-1,6
pool-1-thread-2,9
pool-1-thread-3,8

总结:因为线程池大小为3,每个任务输出indexsleep 2秒,所以每两秒打印3个数字。定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()

newScheduledThreadPool

  创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class P003_Scheduled {

    public static void main(String[] args) {
        ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(3);
        for (int i = 0; i < 10; i++) {
            final int temp = i;
            newScheduledThreadPool.schedule(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName() + ",i:" + temp);
                }
            }, 3, TimeUnit.SECONDS);
        }
    }
}

程序等待了3秒之后才输入如下内容:

执行结果

pool-1-thread-1,i:0
pool-1-thread-2,i:1
pool-1-thread-3,i:2
pool-1-thread-2,i:4
pool-1-thread-1,i:3
pool-1-thread-2,i:6
pool-1-thread-3,i:5
pool-1-thread-2,i:8
pool-1-thread-1,i:7
pool-1-thread-3,i:9

newSingleThreadExecutor

  创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:
  

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class P004_Single {
    public static void main(String[] args) {
        ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int temp = i;
            newSingleThreadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + ",i:" + temp);
                }
            });
        }
    }
}
//注意: 结果依次输出,相当于顺序执行各个任务。

执行结果

pool-1-thread-1,i:0
pool-1-thread-1,i:1
pool-1-thread-1,i:2
pool-1-thread-1,i:3
pool-1-thread-1,i:4
pool-1-thread-1,i:5
pool-1-thread-1,i:6
pool-1-thread-1,i:7
pool-1-thread-1,i:8
pool-1-thread-1,i:9

ThreadPoolExecutor

  Java是天生就支持并发的语言,支持并发意味着多线程,线程的频繁创建在高并发及大数据量是非常消耗资源的,因为Java提供了线程池。在jdk1.5以前的版本中,线程池的使用是及其简陋的,但是在JDK1.5后,有了很大的改善。jdk1.5之后加入了java.util.concurrent包,java.util.concurrent包的加入给予开发人员开发并发程序以及解决并发问题很大的帮助。
  
这里主要介绍下并发包下的Executor接口,Executor是 JDK1.5 时发布的
Executor框架的最顶层实现是ThreadPoolExecutor类,Executors工厂类中提供的newScheduledThreadPoolnewFixedThreadPoolnewCachedThreadPool方法其实也只是ThreadPoolExecutor的构造函数参数不同而已。通过传入不同的参数,就可以构造出适用于不同应用场景下的线程池,ThreadPoolExecutor构造方中的几个参数如下:

corePoolSize: 核心池的大小。 当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中
maximumPoolSize: 线程池最大线程数,它表示在线程池中最多能创建多少个线程;
keepAliveTime: 表示线程没有任务执行时最多保持多久时间会终止。
unit: 参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性。

Principle analysis thread pool

Submit a job to the thread pool thread pool process is as follows:

  1. Judge thread pool of kernel threads if are on a mission, if not (core thread is idle or there is no core thread is created) is working to create a new thread to execute the task. If the core threads are on a mission, then enter the next process.
  2. Thread pool to determine the work queue is full, if the work queue is not full, the task will be stored in the newly submitted job queue. If the work queue is full, then enter the next process.
  3. Judge thread pool thread (here the comparison is the maximum number of threads whether) are in working condition, if not, create a new worker thread to perform the task. If full, then to the saturation strategy to deal with this task.

Custom thread pool thread

  If the current is less than the number of threads in the pool corePoolSize, each to a task, it will create a thread to perform this task;
  if the current number of threads in the pool >=corePoolSize, each to a task, will try to add it to the task queue buffer which, if added successfully, the task will be idle threads waiting to execute it out;
  if add failed (generally a task buffer queue is full), it will try to create a new thread to perform this task;
  if the queue is full , and the total number of threads is not more than maximumPoolSizethe premise, then create a new thread if the current number of threads in the pool reached maximumPoolSize, it will take the task deny policy process;
  if the number of threads in the pool is greater than corePoolSize, if a thread idle for more than keepAliveTime, the thread will be terminated until the number of threads in the thread pool is not greater than corePoolSize; if allowed at the core of the thread pool settings survival time, the thread pool idle time more than the core keepAliveTime, the thread will be terminated.

Sample Code

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class P005_ThreadPoolExecutorTest {

    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3));
        for (int i = 1; i <= 6; i++) {
            TaskThred t1 = new TaskThred("任务" + i);
            executor.execute(t1);
        }
        executor.shutdown();
    }
}

class TaskThred implements Runnable {
    private String taskName;

    public TaskThred(String taskName) {
        this.taskName = taskName;
    }

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

Results of the

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.hao.pool.TaskThred@33909752 rejected from java.util.concurrent.ThreadPoolExecutor@55f96302[Running, pool size = 2, active threads = 1, queued tasks = 0, completed tasks = 4]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
    at com.hao.pool.P005_ThreadPoolExecutorTest.main(P005_ThreadPoolExecutorTest.java:13)
pool-1-thread-2任务5
pool-1-thread-2任务2
pool-1-thread-2任务3
pool-1-thread-2任务4
pool-1-thread-1任务1

The rational allocation of thread pool

CPU-intensive

  CPU密集的意思是该任务需要大量的运算,而没有阻塞CPU一直全速运行。
  CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程),而在单核CPU上,无论你开几个模拟的多线程,该任务都不可能得到加速,因为CPU总的运算能力就那些。

IO密集

  IO密集型,即该任务需要大量的IO,即大量的阻塞。在单线程上运行IO密集型的任务会导致浪费大量的CPU运算能力浪费在等待。所以在IO密集型任务中使用多线程可以大大的加速程序运行,这种加速主要就是利用了被浪费掉的阻塞时间。

如何合理的设置线程池大小。

  要想合理的配置线程池的大小,首先得分析任务的特性,可以从以下几个角度分析:
  
  1. 任务的性质:CPU密集型任务、IO密集型任务、混合型任务。
  2. 任务的优先级:高、中、低。
  3. 任务的执行时间:长、中、短。
  4. 任务的依赖性:是否依赖其他系统资源,如数据库连接等。

  性质不同的任务可以交给不同规模的线程池执行。
  对于不同性质的任务来说,CPU密集型任务应配置尽可能小的线程,如配置CPU个数+1的线程数;
  IO密集型任务应配置尽可能多的线程,因为IO操作不占用CPU,不要让CPU闲下来,应加大线程数量,如配置两倍CPU个数+1;
  而对于混合型的任务,如果可以拆分,拆分成IO密集型和CPU密集型分别处理,前提是两者运行的时间是差不多的,如果处理时间相差很大,则没必要拆分了;
  若任务对其他系统资源有依赖,如某个任务依赖数据库的连接返回的结果,这时候等待的时间越长,则CPU空闲的时间越长,那么线程数量应设置得越大,才能更好的利用CPU
当然具体合理线程池值大小,需要结合系统实际情况,在大量的尝试下比较才能得出,以上只是前人总结的规律。

  最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
  比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5sCPU核心数为8,那么根据上面这个公式估算得到:((0.5+1.5)/0.5)*8=32。这个公式进一步转化为:

  • 最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目

可以得出一个结论

  • 线程等待时间所占比例越高,需要越多线程。
  • **线程CPU时间所占比例越高,需要越少线程。 **

  The number of CPU and IO-intensive tasks set before the thread above formula basically.
  When the CPU-intensive tasks to configure a small number of threads, cpu, and probably quite a few core machine, so that each thread can perform tasks at
  the time IO-intensive, most of the threads are blocked, they need to configure multi-thread count 2 * cpu audit

The name of the operating system to explain:
  some processes spent on computing, while others are waiting most of the time I/Oon spent most of the time,
the former is called computationally intensive (CPU intensive) computer-bound, which is known as I / O I/O-boundintensive .

Guess you like

Origin www.cnblogs.com/haoworld/p/t004bing-fa-bian-chengbing-fa-dui-lie-yu-xian-chen.html