线程
1. 实现方式
- 继承Thread类
- 实现Runnable接口
- 通过callable和future 实现有返回值的线程
public static void main(String[] args) throws Exception {
//Callable的返回值就要使用Future对象,Callable负责计算结果,Future负责拿到结果
//1、实现Callable接口
Callable<Integer> callable = new Callable<Integer>() {
public Integer call() throws Exception {
int i=999;
return i;
}
};
//2、使用FutureTask启动线程
//FutureTask是Runnable的子类RunnableFuture的实现类
FutureTask<Integer> future = new FutureTask<Integer>(callable);
new Thread(future).start();
//3、获取线程的结果
System.out.println(future.get());
}
2. 线程池
在java中提供了Executors类, 用来创建线程池, 大体分为如下几类:
- 创建一个固定大小的线程池
- 使用场景: 线程的内容比较稳定
- 使用多个线程来并发执行提交的任务。底层是个无界队列
static ExecutorService newFixedThreadPool(int nThreads);
- 创建一个可缓存的线程池(可大可小)
- 使用场景: 线程的任务会发生改变
- 创建非固定数量,可缓存的线程池。当提交的任务数量起起伏伏时,会自动创建或者减少执行线程的数量。
static ExecutorService newCachedThreadPool()
- 创建一个定时执行的线程池
- 使用场景: 需要进行定时执行的时候
- scheduleAtFixedRate()
- scheduleWithFixedDelay()
- 区别:
- scheduleAtFixedRate 固定周期执行, 不管上次的线程执行完成没有
- scheduleWithFixedDelay 固定周期执行, 此方法会等待上次线程执行完成后按照周期执行
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) //每秒钟会调用MyRunnable任务, 不管这个MyRunnable有没有执行完成, 都是每秒执行 executorService.scheduleAtFixedRate(new MyRunnable(),0,1000,TimeUnit.MILLISECONDS); //每秒钟会调用MyRunnable任务, 当执行下一次任务, 会先看上一次的任务有没有执行完成, 如果没有, 先等待, 当上一次的任务执行完成以后, 会等待1秒的时候, 执行下一次请求 executorService.scheduleWithFixedDelay(new MyRunnable(),0,1000,TimeUnit.MILLISECONDS);
- 创建一个单线程执行的线程池
//创建单线程,在任务执行时,会依次执行任务。底层是个无界队列。 static ExecutorService newSingleThreadExecutor()
- 创建一个单线程定时执行的线程池
static ScheduledExecutorService newSingleThreadScheduledExecutor() //scheduleAtFixedRate 固定周期执行完毕 //这个也是一个定时执行的线程池, 但是这个线程池中只有一个线程, 如果使用这个方法的话, 那么如果上一个线程没有执行完成, 会直接干掉这个任务, 重新执行任务 executorService.scheduleAtFixedRate(new MyRunnable(),0,1000,TimeUnit.MILLISECONDS); //scheduleWithFixedDelay 上一次执行完毕之后下一次开始执行 executorService.scheduleWithFixedDelay(new MyRunnable(),0,1000,TimeUnit.MILLISECONDS);
队列
1. 原生队列
在java中提供了一个类, queue用来模拟普通原生队列的
- 普通队列的特性:
-
- 先进先出
-
- 队列的头部保存的应该是在队列中存储时间最长的, 队尾应该是存储时间最短的
- 3)新的元素应该插入的队列的尾部, 取出的元素应该是队列的头部
-
- 队列一般不允许随机访问其中的某个元素
-
- 常用方法:
抛出异常 | 返回特殊值 | |
---|---|---|
插入 | add(e) | offer(e) |
移除 | remove() | poll() |
检查 | element() | peek() |
- 插入: add(元素) offer(元素);
- 建议使用offer方法, 一般来说会比add方法更高效
- 移除: remove() poll();
- 建议使用poll进行移除
- 检查: element() peek()
- 检查操作其实就是到队列的头部获取一个内容, 但是不删除
- 建议使用peek() 当获取不到的时候null
2. 双端队列
在java中, 使用Deque双端队列, 顾名思义, 可以分别在队列的两边进行插入和移除数据
- 常用方法
第一个元素(头部) | 最后一个元素(尾部) | |||
---|---|---|---|---|
抛出异常 | 特殊值 | 抛出异常 | 特殊值 | |
插入 | addFirst(e) | offerFirst(e) | addLast(e) | offerLast(e) |
移除 | removeFirst() | pollFirst() | removeLast() | pollLast() |
检查 | getFirst() | peekFirst() | getLast() | peekLast() |
- 一般来说, 我们通常使用的都是返回特殊值的方法
3. 阻塞队列—线程安全
- BlockingQueue
- 阻塞的原生队列
抛出异常 | 特殊值 | 阻塞 | 超时 | |
---|---|---|---|---|
插入 | add(e) | offer(e) | put(e) | [offer(e, time, unit) ](…/…/…/java/util/concurrent/BlockingQueue.html#offer(E, long, java.util.concurrent.TimeUnit)) |
移除 | remove() | [poll() ](…/…/…/java/util/concurrent/BlockingQueue.html#poll(long, java.util.concurrent.TimeUnit)) | take() | [poll(time, unit) ](…/…/…/java/util/concurrent/BlockingQueue.html#poll(long, java.util.concurrent.TimeUnit)) |
检查 | element() | peek() | 不可用 | 不可用 |
- BlockingDeque
- 阻塞的双端队列
第一个元素(头部) | ||||
---|---|---|---|---|
抛出异常 | 特殊值 | 阻塞 | 超时期 | |
插入 | addFirst(e) | offerFirst(e) | putFirst(e) | [offerFirst(e, time, unit) ](…/…/…/java/util/concurrent/BlockingDeque.html#offerFirst(E, long, java.util.concurrent.TimeUnit)) |
移除 | removeFirst() | [pollFirst() ](…/…/…/java/util/concurrent/BlockingDeque.html#pollFirst(long, java.util.concurrent.TimeUnit)) | takeFirst() | [pollFirst(time, unit) ](…/…/…/java/util/concurrent/BlockingDeque.html#pollFirst(long, java.util.concurrent.TimeUnit)) |
检查 | getFirst() | peekFirst() | 不适用 | 不适用 |
最后一个元素(尾部) | ||||
抛出异常 | 特殊值 | 阻塞 | 超时期 | |
插入 | addLast(e) | offerLast(e) | putLast(e) | [offerLast(e, time, unit) ](…/…/…/java/util/concurrent/BlockingDeque.html#offerLast(E, long, java.util.concurrent.TimeUnit)) |
移除 | removeLast() | pollLast() | takeLast() | [pollLast(time, unit) ](…/…/…/java/util/concurrent/BlockingDeque.html#pollLast(long, java.util.concurrent.TimeUnit)) |
检查 | getLast() | peekLast() | 不适用 | 不适用 |
阻塞的方法:
- put(e);
- put方法, 如果队列容量满了, 那么此方法会进行永久等待
- take();
- take方法, 如果队列当中没有了元素, 那么此方法会进行永久等待
超时的方法:
- offer(e,time,unit);
- 带时间参数的offer方法, 如果队列容量满了, 那么此方法会在规定的时间内进行等待
- poll(time,unit);
- 带时间参数的poll方法, 如果队列没有了元素, 那么此方法会 规定时间内进行等待
推荐使用的方式是: 阻塞的方法和超时的方法,这两套方法都是线程安全的方法