thread和Runnable,这里是Callable的使用
面试官有问题就是想要有返回值的线程怎么办
public class CallableDemo implements Callable<String> {
@Override
public String call() throws Exception {
int priority = Thread.currentThread().getPriority();
System.out.println(priority);
return "线程执行";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1种实现
FutureTask<String> futureTask = new FutureTask<>(new CallableDemo());
new Thread(futureTask).start();
String s = futureTask.get();
System.out.println(s);
//线程池的使用
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<?> submit = executorService.submit(new CallableDemo());
Object o = submit.get();
System.out.println(o.toString());
executorService.shutdown();
}
}
线程池的种类
public class ThreadPool {
public static void main(String[] args) {
/**
* 1、newCachedThreadPool不限量
* 创建一个线程池,如果线程池中的线程数量过大,它可以有效的回收多余的线程,如果线程数不足,那么它可以创建新的线程。
* 不足:这种方式虽然可以根据业务场景自动的扩展线程数来处理我们的业务,但是最多需要多少个线程同时处理缺是我们无法控制的;
* 优点:如果当第二个任务开始,第一个任务已经执行结束,那么第二个任务会复用第一个任务创建的线程,并不会重新创建新的线程,提高了线程的复用率;
*/
ExecutorService executorService = Executors.newCachedThreadPool();
/**
* 2、newFixedThreadPool
* 优点: 两个结果综合说明,newFixedThreadPool的线程数是可以进行控制的,
* 因此我们可以通过控制最大线程来使我们的服务器打到最大的使用率,
* 同事又可以保证及时流量突然增大也不会占用服务器过多的资源。
* 超出的线程会在队列中等待。
*/
ExecutorService executor = Executors.newFixedThreadPool(1);
/**
* 3、newScheduledThreadPool
* 该线程池支持定时,以及周期性的任务执行,我们可以延迟任务的执行时间,也可以设置一个周期性的时间让任务重复执行。 该线程池中有以下两种延迟的方法。
*
*/
ScheduledExecutorService s = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 3;i++) {
s.schedule(()->
{System.out.println("123");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},2, TimeUnit.SECONDS);
}
/**
*
* 单个线程
*/
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 3;i++) {
//execute
executorService1.submit(()->
{System.out.println("99999");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
}
创建ThreadPoolExecutor
new ThreadPoolExecutor(2, 10, 20, TimeUnit.SECONDS, new ArrayBlockingQueue(10),
new ThreadPoolExecutor.DiscardOldestPolicy());
//-------------------------------带有名字的线程池线程 namedFactory 定义 thread名字等
private static ThreadFactory namedFactory = new ThreadFactoryBuilder().setNameFormat("线程-demo-%d").build();
private static ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 4, 60, TimeUnit.SECONDS, new
ArrayBlockingQueue<>(10), namedFactory, new ThreadPoolExecutor.AbortPolicy());
参数
corePoolSize - 池中所保存的线程数,包括空闲线程。
maximumPoolSize - 池中允许的最大线程数(采用LinkedBlockingQueue时没有作用)。
keepAliveTime -当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间,线程池维护线程所允许的空闲时间。
unit - keepAliveTime参数的时间单位,线程池维护线程所允许的空闲时间的单位:秒 。
workQueue - 执行前用于保持任务的队列(缓冲队列)。此队列仅保持由execute 方法提交的 Runnable 任务。
RejectedExecutionHandler -线程池对拒绝任务的处理策略(重试添加当前的任务,自动重复调用execute()方法)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
流程和生命周期
当进来一个任务后,如果正在运行的线程数量小于corePoolSize,则立即执行此任务
如果正在运行的线程数量大于或者等于corePoolSize,那么将这个任务放入workQueue
如果这时候队列满了并且正在运行的线程数还小于maximumPoolSize,那么就会创建非核心线程来执行这个任务
如果队列满了并且正在运行的线程数大于等于maximumPoolSize,那么线程池就会启动拒绝策略RejectedExecutionHandler handler
当一个线程无事可做一段时间keepAliveTime,线程就会判断
如果当前线程数大于corePoolSize,那么这个线程被停掉
所有线程池的任务完成后,就会收缩至corePoolSize大小
拒绝策略
在进来的任务数量大于maximumPoolSize + workQueue时,就会触发拒绝策略,线程池默认使用的是AbortPolicy拒绝策略
AbortPolicy:
默认拒绝策略,直接抛出 java.util.concurrent.RejectedExecutionException 异常阻止系统正常运行
CallerRunsPolicy:
该策略既不会抛出任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量
DiscardOldestPolicy:
抛弃队列中等待最久的任务,然后将当前任务加入到队列中,在尝试提交当前任务
DiscardPolicy:
将某些任务回退到调用者,从而降低新任务的流量
DiscardOldestPolicy:
抛弃队列中等待最久的任务,然后将当前任务加入到队列中,在尝试提交当前任务
DiscardPolicy:
该策略默默丢弃无法处理的任务,不予任何处理也不抛出异常,如果允许任务丢失,这是最好的一种策略
线程池的工作队列:
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue:一个由链表结构组成的无界阻塞队列。
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
DealyQueue:一个使用优先级(启动时间)队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
---------------- 常用的3种
SynchronousQueue
SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素 没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素 阻塞等待消费
非公平和公平 即非公平模式下采用的是后进先出的顺序
代码执行就理解了
package com.zhk.jucstudy;
import java.util.concurrent.SynchronousQueue;
public class Demo {
public static void main(String[] args) {
//SynchronousQueue<Integer> queue=new SynchronousQueue<>(false);非公平
final SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>();
Thread putThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("put thread start");
try {
Thread.sleep(10000);
queue.put(1);
} catch (InterruptedException e) {
}
System.out.println("put thread end");
}
});
Thread takeThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("take thread start");
try {
System.out.println("take from putThread: " + queue.take());
} catch (InterruptedException e) {
}
System.out.println("take thread end");
}
});
putThread.start();
takeThread.start();
}
}
LinkedBlockingQueue
LinkedBlockingQueue是一个无界缓存等待队列, 队列容量不足或为0时自动阻塞。
当前执行的线程数量达到corePoolSize的数量时,剩余的元素会在阻塞队列里等待。(所以在使用此阻塞队列时maximumPoolSizes就相当于无效了),每个线程完全独立于其他线程。生产者和消费者使用独立的锁来控制数据的同步,即在高并发的情况下可以并行操作队列中的数据。
此队列按FIFO(先进先出)排序元素
新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素
链接队列的吞吐量通常要高于基于数组的对列(ArrayBlockingQueue),但是在大多数并发应用程序中,其可预知的性能要低
可选的容量范围构造方法参数作为防止队列过度扩展的一种方法,如果未指定容量,则等于Integer.MAX_VALUE,除非插入节点会使队列超出容量,否则每次插入后会动态地创建链接节点
ArrayBlockingQueue
是一个有界缓存等待队列,可以指定缓存队列的大小,当正在执行的线程数等于corePoolSize时,多余的元素缓存在ArrayBlockingQueue队列中等待有空闲的线程时继续执行,当ArrayBlockingQueue已满时,加入ArrayBlockingQueue失败,会开启新的线程去执行,当线程数已经达到最大的maximumPoolSizes时,再有新的元素尝试加入ArrayBlockingQueue时会报错