(1)从空的阻塞队列中读取元素,将会阻塞,知道其他线程插入元素到这个队列中。
(2)往满的队列中添加元素,同样也会阻塞,知道有线程从队列中取出元素或者队列中的元素被清除。
(1)ArrayDeque, (数组双端队列)
(2)PriorityQueue, (优先级队列)
(3)ConcurrentLinkedQueue, (基于链表的并发队列)
(4)DelayQueue, (延期阻塞队列)(阻塞队列实现了BlockingQueue接口)
(5)ArrayBlockingQueue, (基于数组的并发阻塞队列)
(6)LinkedBlockingQueue, (基于链表的FIFO阻塞队列)
(7)LinkedBlockingDeque, (基于链表的FIFO双端阻塞队列)
(8)PriorityBlockingQueue, (带优先级的无界阻塞队列)
(9)SynchronousQueue (并发同步阻塞队列)
ConcurrentLinkedQueue :是一个基于高并发场景下的队列,通过无锁的方式实现了高并发下的高性能。他是一个聚集链接节点的无界安全队列。不允许有null元素。
ConcurrentLinkedQueue重要方法:
add 和offer() 都是加入元素的方法(在ConcurrentLinkedQueue中这俩个方法没有任何区别)
poll() 和peek() 都是取头元素节点,区别在于前者会删除元素,后者不会。
/**
*
* @author johson
*阻塞队列和非阻塞队列
*阻塞队列的最大好处就是能防止队列容器溢出,防止数据丢失
*/
public class test01 {
public static void main(String[] args){
//新建一个非阻塞式队列,无界的
ConcurrentLinkedQueue concurrentLinkedQueue = new ConcurrentLinkedQueue<>();
concurrentLinkedQueue.add("张三"); //add()调用了offer方法
//concurrentLinkedQueue.offer("王麻子");
concurrentLinkedQueue.add("李四");
concurrentLinkedQueue.add("王五");
System.out.println("peek()方法showtime");
//peek()是获取但不删除
System.out.println(concurrentLinkedQueue.peek());
System.out.println(concurrentLinkedQueue.peek());
System.out.println(concurrentLinkedQueue.peek());
System.out.println("pool()方法showtime");
//一次性只能获取一个值,poll()是获取一个删除一个
System.out.println(concurrentLinkedQueue.poll());
System.out.println(concurrentLinkedQueue.poll());
System.out.println(concurrentLinkedQueue.poll());
System.out.println(concurrentLinkedQueue.size());
}
}
peek()方法与poll()方法运行对比:
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:
(1)在队列为空时,获取元素的线程会等待队列变为非空。
(2)当队列满时,存储元素的线程会等待队列可用。
阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
阻塞队列的简单实验:
/**
*
* @author johson
* 有界队列,阻塞队列
*/
public class test02 {
//阻塞式队列:存队列的时候,如果满了。就会等待;取队列的时候,如果没有值或者取不到值也会等待
public static void main(String[] args) throws InterruptedException{
//新建一个大小为3的阻塞队列
BlockingQueue blockingDeque = new ArrayBlockingQueue(3);
//添加非阻塞队列
blockingDeque.offer("张三");
//添加阻塞式队列,如果队列已经满了,就会等待3秒,如果还是满的就会结束插入操作
blockingDeque.offer("李四",3,TimeUnit.SECONDS);
System.out.println(blockingDeque.poll());
System.out.println(blockingDeque.poll(3,TimeUnit.SECONDS));
//获取阻塞式队列,过了三秒没有获取到就不再等待
System.out.println(blockingDeque.poll(3,TimeUnit.SECONDS));
}
}
利用BlockingQueue实现生产者与消费者实验:
/**
* 生产者线程
* @author johson
*
*/
class ProduceThread implements Runnable{
private BlockingQueue blockingQueue;
private volatile boolean flag = true;
//计数器,原子类
AtomicInteger atomicInteger = new AtomicInteger();
public ProduceThread(BlockingQueue blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
System.out.println("生产者线程启动");
try {
while(flag){
//基于线程安全的一个自增方法
String data = atomicInteger.incrementAndGet()+"";
boolean offer = blockingQueue.offer(data,2,TimeUnit.SECONDS);
if(offer){
System.out.println("生产者入列成功,data=" + data);
}
else {
System.out.println("生产者入列失败,data=" + data);
}
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//停止方法
public void stop(){
this.flag =false;
}
}
/**
* 消费者线程
* @author johson
*
*/
class ConsumerThread implements Runnable{
private BlockingQueue blockingQueue;
private volatile boolean flag = true;
public ConsumerThread(BlockingQueue blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
try {
while (flag) {
String data = blockingQueue.poll(2, TimeUnit.SECONDS);
if(data == null){
System.out.println("消费者超过2秒时间没有获取到信息");
flag = true;
return ;
}
System.out.println("消费者获取到data="+data);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
System.out.println("消费者已经停止");
}
}
}
public class test03 {
public static void main(String[] args) {
BlockingQueue blockingQueue = new LinkedBlockingQueue(10);
ProduceThread produceThread = new ProduceThread(blockingQueue);
ConsumerThread consumerThread = new ConsumerThread(blockingQueue);
Thread t1 = new Thread(produceThread);
Thread t2 = new Thread(consumerThread);
t1.start();
t2.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
produceThread.stop();
}
}
实验结果:
线程池是为了突然大量爆发的线程而设计的,通过有限的几个固定线程为大量的操所服务,减少了线程创建和销毁的时间,从而提高效率。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。复用原来的线程(但是run()方法体不一样了)
第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用。
Java通过Executors(jdk1.5并发包)提供四种线程池,分别为:
(1)newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
/**
* newCachedThreadPool创建一个可缓存线程池
* @author johson
*
*/
public class test04 {
public static void main(String[] args) {
//创建了一个可缓存的线程池重复利用
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
for(int i = 0;i < 20;i++){
final int t = i;
newCachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" "+t);
}
});
}
}
}
(2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
/**
* newFixedThreadPool 创建一个定长线程池
* @author johson
*
*/
public class test05 {
public static void main(String[] args) {
ExecutorService newCachedThreadPool = Executors.newFixedThreadPool(3);
for(int i = 0;i < 20;i++){
final int t = i;
newCachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" "+t);
}
});
}
}
}
(3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
/**
* newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
* @author johson
*
*/
public class test06 {
public static void main(String[] args) {
ScheduledExecutorService newCachedThreadPool = Executors.newScheduledThreadPool (10);
for(int i = 0;i < 20;i++){
final int t = i;
newCachedThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" "+t);
}
},3,TimeUnit.SECONDS);//3秒后执行调度
/* newCachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" "+t);
}
});*/
}
}
}
(4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
/**
* newSingleThreadExecutor 创建一个单线程化的线程池
* @author johson
*
*/
public class test07 {
public static void main(String[] args) {
ExecutorService newCachedThreadPool = Executors.newSingleThreadExecutor();
for(int i = 0;i < 10;i++){
final int t = i;
newCachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" "+t);
}
});
}
}
}
常用的四个线程池都是通过ThreadPoolExecutor()封装的
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
corePoolSize:核心线程数
maximumPoolSize:最大线程数
核心线程数与最大线程数的区别:核心线程数是指实际用的线程数,最大线程数是指最多能创建的线程数
最多支持的线程数=最大线程数+队列长度
/**
* 自定义线程池
* @author johson
*
*/
class TaskThread implements Runnable{
private String threadName;
public TaskThread(String threadName) {
this.threadName = threadName;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" "+threadName);
}
}
public class test08 {
public static void main(String[] args) {
//核心线程数为1(最多运行的线程数),最大线程数为2(最多只能创建几个线程),线程空闲超时时间,分钟还是秒,队列为3个
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1, 2, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue(3));
//任务1创建线程,正在执行
executor.execute(new TaskThread("任务1"));
//任务2存放在队列缓存中,因为只有一个核心线程
executor.execute(new TaskThread("任务2"));
//任务3存放在队列缓存中,因为只有一个核心线程
executor.execute(new TaskThread("任务3"));
executor.execute(new TaskThread("任务4"));
//再创建一个线程,因为最大能创建的线程数为2
executor.execute(new TaskThread("任务5"));
//任务6会报错,拒绝策略为线程数>最大线程数+队列长度
//executor.execute(new TaskThread("任务6"));
}
}
表示该任务需要大量的IO,即大量的阻塞。
表示该任务需要大量的运算,而没有阻塞,CPU一直全速运行。
CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程),而在单核CPU上,无论你开几个模拟的多线程,该任务都不可能得到加速,因为CPU总的运算能力就那些。
CPU密集型时,任务可以少配置线程数,大概和机器的cpu核数相当,这样可以使得每个线程都在执行任务
IO密集型时,大部分线程都阻塞,故需要多配置线程数,2*cpu核数