目录
2、可打断锁(lock.lockInterruptibly();)
2、固定大小的线程池newfFixedThreadPool(n)
3、带缓冲功能的线程池newCachedThreadPool()
4、单线程线程池newSingThreadExecutor()
2、任务调度线程池newScheduledThreadPool(n)
- public void test01(){
- Thread t=new Thread(){
- @Override
- public void run() {
- log.info("线程执行");
- }
- }; t.start();}
- public void test02() {
- Thread t = new Thread(new Runnable() {
- @Override
- public void run() {
- log.info("线程执行");
- }
- });
- t.start();
-
- }
用Runnable 容易与线程池等高级API配合
用Runnable 让任务脱离了Thread继承体系,更灵活
- static void RunnableTest2() throws ExecutionException, InterruptedException {
- // 返回值类型
- FutureTask
task=new FutureTask<>(new Callable() { - @Override
- public Integer call() throws Exception {
- System.out.println("running...");
- Thread.sleep(4000);
- return 100;
- }
- }); Thread t=new Thread(task,"t2");
- t.start(); // 阻塞 直到获得task的返回值
- System.out.println(task.get());
- }
- start()
- run()
- join() //等待线程运行结束
- join(long n) //等待线程运行结束 最多等待n毫秒
- getId()
- getName()
- setName()
- getPriority()
- setPriority() //修改优先级
- getState() //获取线程状态 NEW/RUNNABLE/TIMED_WAITING/TERMINATED等
- isInterrupted() 判断是否被打段,不会清除打断标记
- isAlive() 线程是否存活(是否允许结束)
- interrupt() 打断线程 叫醒线程--抛出InterruptedException 异常
- interrupted() --Thread判断当前线程是否被打断 会清除打断标记
- currentThread()--Thread获取当前正在执行的线程 类似this
- sleep(ms) --Thread 睡眠
- yield() --Thread 把当前线程资源让给其他线程
Thread.sleep
- sleep
- 睡眠---调用sleep会让当前线程进入睡眠状态--TIMED_WAITING状态(阻塞)
- Thread.sleep(2000);
- 也可以使用
- TimeUnit.SECONDS.sleep(2); // 睡眠2秒
Thread.yield()
调用yield会让当前线程从Running进入Rnnable 就绪状态(停下来),然后调度执行其他线程
- 优先级是指有更多的机会,
- Thread.yield(); // 当前线程资源让给其他线程
- thread.setPriority(Thread.MAX_PRIORITY); // 设置线程的优先级 [1,10]
t1.join();
- 为什么需要join
- jion等待线程结束
- t1.start(); // 启动t1
- t1.join(); // 主线程等待t1结束
- t1.join(2000); // 最多等待2秒
打断sleep, wait,join
对于sleep, wait,join被打断后会清空打断标记thread.isInterrupted()为fales
打断后 抛出InterruptedException 异常
打断普通线程,时候没有效果,只是一个标记为true,需要自己结束
- public static void test5() throws InterruptedException {
- Thread thread = new Thread("t1") {
- @Override
- public void run() {
- while (true){
- System.out.println(1);
- if (Thread.currentThread().isInterrupted()){
- // 被打断
- System.out.println("被打断");
- break;
- }
- }
- }
- };
- thread.start();
- TimeUnit.SECONDS.sleep(1);
- thread.interrupt(); // 不能直接使线程停下,只是标记
- System.out.println("打断结果状态:"+thread.isInterrupted());
- }
打断park()线程
LockSupport.park(); //阻塞当前线程
- public static void test6() throws InterruptedException {
- Thread thread = new Thread("t1") {
- @Override
- public void run() {
- while (true){
- System.out.println("运行pack线程");
- LockSupport.park(); //阻塞标记为false时候才会生效--阻塞当前线程
- System.out.println("重新执行");
- }
- }
- };
- thread.start();
- TimeUnit.SECONDS.sleep(1);
- thread.interrupt(); // 打断park
- System.out.println("打断结果状态:"+thread.isInterrupted());
- }
- public static void test6() throws InterruptedException {
- Thread thread = new Thread("t1") {
- @Override
- public void run() {
- while (true){
- System.out.println("运行pack线程");
- LockSupport.park(); //阻塞当前线程
- System.out.println("重新执行");
- Thread.interrupted(); //重新把标记设置为 false ; LockSupport.park()再次阻塞 }
- }
- };
- thread.start();
- TimeUnit.SECONDS.sleep(1);
- thread.interrupt(); // 打断park
- System.out.println("打断结果状态:");
- }
不推荐使用的方法
这些方法已经过时,容易破坏同步代码块,造成线程死锁
- stop() 停止线程运行
- suspend() 挂起(暂停线程)
- resume() 恢复线程运行
默认情况下,java进程需要的等待所以线程都运行结束,才会结束
但是主线程结束后,如果有守护线程在运行,即使守护线程还在执行,那么程序也会结束
即主线程可以不用管守护线程死活
- thread.setDaemon(true); // 设置成守护线程
- thread.start();
垃圾回收器就是一种守护线程
Tomcat中Acceptor和Poller线程都是守护线程
- 初始状态、可运行状态(就绪状态)、运行状态、阻塞状态、终止状态
- java层面NEW(创建)、RUNNABLE(运行,可运行,阻塞)、BLOCKED、WAITING、TIMED_WAITING(睡眠)、TERMINATED
多个线程对共享资源的读写操作
一段代码块如果存在对共享资源的多线程读写操作,称为这段代码为临界区
原子类和synchronized
对象锁
内部数据会上锁,相同synchronized(对象)片段只允许一个线程操作,其他的会被阻塞
- static int counter=0;
- static Object lock=new Object(); // 对象锁
-
- public static void test9() throws InterruptedException {
- Thread t1 = new Thread("t1") {
- @Override
- public void run() {
- for (int i = 0; i < 5000; i++) {
- synchronized (lock) { // 内部数据会上锁,相同lock 只允许一个线程操作,其他的会被阻塞
- counter++;
- }
- }
- }
-
- };
- Thread t2 = new Thread(() -> {
- for (int i = 0; i < 5000; i++) {
- synchronized (lock){
- counter--;
- }
-
- }
-
- }, "t2");
- t1.start();
- t2.start();
- System.out.println("结果:"+counter);
- }
synchronized实际是用对象锁保证了临界区内代码的原子性
原子性:不可分割,安全
- class Room{
- private int countt=0;
- public void add(){ //安全加法
- synchronized (this){
- countt++;
- }
- }
- public void sub(){ // 安全减法
- synchronized (this){
- countt--;
- }
- }
- public int get(){ // 安全读取
- synchronized (this){
- return this.countt;
- }
- }
-
- }
加在成员方法上相当于synchronized(this)
- class Room{
- private int countt=0;
- public synchronized void add(){
- countt++;
- }
- public synchronized void sub(){
- countt--;
- }
- public synchronized int get(){
-
- return this.countt;
- }
-
- }
如果加载static 方法上,那么想到他于 synchronized(Room.class)
对于单例类使用synchronized(this) 对于多例使用synchronized(xx.class)
- 如果没有共享,则线程安全
- 如果被共享了
- 只有读操作,则线程安全,
- 如果有读写,则这段代码是临界区,需要考虑线程安全
- 局部变量是否线程安全
- 局部变量是线程安全的
- 但局部变量的引用的对象则未必线程安全
- 局部变量的线程安全
- String
- Integer 等包装类
- StingBuffer
- Random
- Vector List<Integer> list=new Vector<>(); // 线程安全集合
- Hashtable private Map
boxes=new Hashtable<>(); - java.util.concurrent包下的类(juc)
调用wait方法,即可进入WaitSet变为WAITING状态
BLOCKED(等待锁)和WAITING(放弃)的线程都处于阻塞状态,不占用CPU的时间片
WAITING线程在调用notify或者notifyAll时候唤醒,但唤醒后并不会立刻获得锁,任需要竞争
方法:
- synchronized (obj){
- obj.wait() 一直等待 --释放锁
- obj.wait(2000) 等待两秒--释放锁 ,两秒后自动执行 obj.notify(),锁任需要竞争
- obj.notify() 唤醒一个等待线程 ,锁任需要竞争
- lock.notifyAll() 唤醒全部等待线程 ,锁任需要竞争
- }
例子:
- static final Object lock=new Object();
- public static void main(String[] args) throws InterruptedException {
- new Thread(()->{
- synchronized (lock){
- System.out.println("t1执行...");
- try {
- lock.wait(); // 释放lock锁 ,并进入等待队列A
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- System.out.println("t1其他代码...");
- }
-
- },"t1").start();
- new Thread(()->{
- synchronized (lock){
- System.out.println("t2执行...");
- try {
- lock.wait(); // 释放lock锁 ,并进入等待队列A
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- System.out.println("t2其他代码...");
- }
- },"t2").start();
- TimeUnit.SECONDS.sleep(2);
- synchronized (lock){
- // lock.notify(); // 主线程获得锁后唤醒一个线程
- lock.notifyAll(); // 唤醒全部lock等待 线程
- }
- }
sleep是Thread方法,wait是object方法
sleep可以直接使用,wait需要配合synchronized使用
sleep不会释放对象锁,wait会释放对象锁
- 基本使用:
- 它们是LockSupport类中的方法
- 暂停当前线程
- LockSupport.park();
-
- // 恢复某个线程的运行--可以在LockSupport.park(); 之前调用
- LockSupport.unpark(t1)
- 与wait和notify相比
- wait、notify和notifyAll需要配合 synchronized 使用
- LockSupport.park() 不会释放锁
- LockSupport.unpark(t1) 可以先调用
死锁
t1线程获得A对象锁,接下来想获得B对象锁
t2线程获得B对象锁,接下来想获得A对象锁
命令查看锁
- jps -- 查看运行的进程 id
- jstack 进程id -- 查看线程信息 --可以查看死锁信息
活锁
线程互相改变自身的结束条件,两个线程都不能结束
饥饿
一个线程的优先级太低,始终得不到CPU调度执行,也不能够结束
- 对比synchronized
- 可中断
- 可以设置超时时间
- 可以设置为公平锁
- 支持多个条件变量
- 与synchronized一样,都可以支持可重入
- 是指一个线程如果首次获得了这把锁,那么再释放后有机会再次获得
- 同一个线程可以多次获取该锁,每次获取都需要对应的释放操作。
- 当一个线程已经持有锁时,再次获取锁时不会造成死锁,而是增加锁的计数器。
- 锁的计数器会记录锁被同一个线程持有的次数,只有计数器为0时,其他线程才能获取该锁。
可重入测试例子: 主线程在 lock.lock(); 下又掉用同一个需要锁的方法,么有发送阻塞
- public class Test5 {
- private static ReentrantLock lock=new ReentrantLock();
- public static void main(String[] args) {
- lock.lock(); // 锁
- try{
- System.out.println("主方法");
- m1();
- }finally {
- lock.unlock(); // 释放锁
- }
- }
-
- public static void m1(){
- lock.lock();
- try{
- System.out.println("m1进入");
- }finally {
- lock.unlock();
- }
- }
- public static void m2(){
- lock.lock();
- try{
- System.out.println("m2进入");
- }finally {
- lock.unlock();
- }
- }
- }
lock.lock(); // 不可打断的锁--阻塞
lock.lockInterruptibly(); // 可以被打断的锁-阻塞 可以被 t1.interrupt(); 打断锁阻塞状态
- private static ReentrantLock lock=new ReentrantLock();
- public static void main(String[] args) throws InterruptedException {
- Thread t1= new Thread(()->{
- try{
- // 如果没有竞争此法就会获得锁
- // 如果有竞争被阻塞,可以被interrupt() 方法打断阻塞状态--并且抛出异常
- System.out.println("尝试获得锁...");
- lock.lockInterruptibly(); // 可以被打断的锁-阻塞
- System.out.println("获得锁");
- } catch (InterruptedException e) {
- System.out.println("被打断");
- throw new RuntimeException("没有获得锁,被打断了");
- }
- System.out.println("抛出锁");
- lock.unlock();
- });
- lock.lock();
- t1.start();
- Thread.sleep(1000);
- System.out.println("打断t1");
- t1.interrupt();
- }
lock.tryLock() 无参数 --不会阻塞---形式标识当前是否获得锁,获得锁放回true
lock.tryLock(1, TimeUnit.SECONDS) //阻塞等待1秒(时间,单位),--获得锁后为true
这个方法也是可以使用 t1.interrupt(); 打断的
- private static ReentrantLock lock=new ReentrantLock();
- public static void main(String[] args) throws InterruptedException {
-
- Thread t1=new Thread(()->{
- System.out.println("尝试获得锁");
- try {
- if(!lock.tryLock(1, TimeUnit.SECONDS)){ // 等待1秒,获得锁后为true System.out.println("获得不到锁"); return; }
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- try {
- System.out.println("获得到锁");
- }finally {
- lock.unlock();
- }
- },"t1");
- lock.lock();
- System.out.println("主线程获得锁");
- t1.start();
- Thread.sleep(500);
- lock.unlock();
- }
例如 :哲学家吃饭避免死锁问题
- public class Test8 {
- public static void main(String[] args) {
- ReentrantLock c1=new ReentrantLock();
- ReentrantLock c2=new ReentrantLock();
- ReentrantLock c3=new ReentrantLock();
- ReentrantLock c4=new ReentrantLock();
- ReentrantLock c5=new ReentrantLock();
- new Re("苏格拉底",c1,c2).start();
- new Re("柏拉图",c2,c3).start();
- new Re("亚里士多德",c3,c4).start();
- new Re("拉克",c4,c5).start();
- new Re("阿吉姆",c5,c1).start();
- }
- }
- class Re extends Thread{
-
- ReentrantLock rt;
- ReentrantLock lt;
- public Re(String name,ReentrantLock rt,ReentrantLock lt){
- super(name);
- this.rt=rt;
- this.lt=lt;
- }
- @Override public void run() {
- while (true) {
- // 获得左筷子
- if(lt.tryLock()){
- // 获得右手筷子
- if(rt.tryLock()){
- System.out.println(Thread.currentThread().getName()+"正在吃饭");
- rt.unlock();
- lt.unlock();
- }else {
- lt.unlock();
- }
- }
- }
- }
- }
ReentrantLock默认是不公平锁--争抢锁不是按阻塞队列执行,
ReentrantLock c1=new ReentrantLock(true); //设置公平锁,按阻塞队列顺序获得锁
公平锁一般没必要,会降低并发度
支持多个条件变量
类似wait/notify
- // 创建一个条件变量(线程休息室)
- Condition condition=lock.newCondition();
- Condition condition2=lock.newCondition();
- lock.lock(); // 抢锁
- condition.await(); // 线程等待 --释放锁--阻塞 和wait一样 可以被打断(被打断会抛出异常),可以设置超时时间
- condition.signal(); // 唤醒一条condition 休息室的线程 ,唤醒后也需要从新竞争锁
- condition.signalAll(); // 唤醒condition 里的所有等待线程
可见性
用来修饰成员变量和静态成员变量,每次都获取最新值
但并没有解决指令交错的问题
volatile 可以避免指令的重排序
写屏障:对共享变量(包括之前的变量)的改变都会同步到主存中
读屏障:读取从主存中读取
但并没有解决指令交错的问题
对于在synchronized外的变量使用volatile关键字
饿汉式:类加载就会导致该单例对象被创建
懒汉方:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建
例子:饿汉式
- public final class SingLeton implements Serializable {
-
- private SingLeton(){
-
- }
-
- public SingLeton getSingLeton(){
- return INSTANCE;
- }
- private static SingLeton INSTANCE=new SingLeton();
- // 防止序列化反序列化创建对象
- public Object readResolve(){
- return INSTANCE;
- }
- }
例子:懒汉式
- public class Sing2 {
- private Sing2(){
-
- }
- private static class Test {
- static final Sing2 InINSTANCE=new Sing2();
- }
- public Sing2 getINSTANCE(){
- return Test.InINSTANCE;
- }
- }
- public void withdraw(Integer amount){
-
- while (true){ // 在最新的基础上修改
- // 获取 最新值
- int prev=balance.get(); // 修改越热
- int next=prev-amount; // 真正的修改-- 比较并设置 CAS
- if( balance.compareAndSet(prev,next)){
- // 如果 成功set 就退出循环--否则继续
- break;
- }
- }
- }
CAS必须借助volatile才能读取到共享变量的最新值来实现比较并交换的效果
CAS特点
可以实现无锁并发,适用于线程少,多核CPU的场景
数据类型
AtomicBoolean
AtomicInteger
AtomicLong
方法
- i.compareAndSet(oldValue,newValue) 如果没有被修改过才执行赋值
- i.incrementAndGet(); ++i
- i.getAndIncrement() i++
- i.decrementAndGet(); --i
- i.getAndDecrement() +--
- i.addAndGet(10) 先+10 再get
- 自定义运算
- int i1 = i.updateAndGet(x -> 1); // x代表当前值, 先计算,再get
- int i2=i.getAndUpdate(x2->7); // 先获取旧值 , 再计算
例如
- public void withdraw(Integer amount){
-
- while (true){ // 在最新的基础上修改
- // 获取 最新值
- int prev=balance.get(); // 修改越热
- int next=prev-amount; // 真正的修改-- 比较并设置 CAS
- if( balance.compareAndSet(prev,next)){
- // 如果 成功set 就退出循环--否则继续
- break;
- }
- }
- }
- 可以改成:
- public void withdraw(Integer amount){
- this.balance.addAndGet(-1*amount); // 保证原子性
- }
AtomicReference 普通
AtomicMarkableReference 不关心引用被更改了几次,只关心
AtomicStampedReference 版本号
- private AtomicReference<BigCount> reference=new AtomicReference<>(balance);
- BigCount pre=this.reference.get();
- BigCount next=new BigCount(pre.value-amount) ;
- reference.compareAndSet(pre,next) // 主线程无法得知balance 是否从A-->B-->A
-
-
- private AtomicStampedReference<BigCount> reference=new AtomicStampedReference<>(balance,0); // 包装balance ,以及设置版本号
- BigCount pre=this.reference.getReference();
- BigCount next=new BigCount(pre.value-amount) ;
- int stamp = reference.getStamp(); // 获取版本号
- this.reference.compareAndSet(pre,next,stamp,stamp+1) // 对比旧值和版本号 都一致才会执行Set
-
- private AtomicStampedReference<BigCount> reference=new AtomicStampedReference<>(balance,true); // 包装balance ,以及设置版本号
- this.reference.compareAndSet(pre,next,true,false) // 对比旧值和状态值,一致才会执行成功
- 拉姆达表达式
- Supplier ()->结果
- function (参数)->结果 一个参数一个结果
- BIFunction ( 参数1,参数2)-> 结果,两个参数一个结果
- consumer 消费者 一个参数没结果 (参数)->void
- BiConsumer (参数1,参数2)->void
AtomicIntegerArray 保护Integer数组
AtomicLognArray 保护long数组
AtomicReferenceArray 保护的引用数组
- AtomicIntegerArray A=new AtomicIntegerArray(new int[]{1});
- A.updateAndGet(0,(a1)->++a1); 请使用 updateAndGet 对数据进行修改,set方法不具备线程安全
AtomicReferenceFieIdUpdater 引用类型 字段
AtomicIntegerFieIdUpdater 保护 Integer 字段
AtomicLongFieIdUpdater 保护Long 字段
- 保护类的属性 ,属性需要设置成volatile 类型
- Student student=new Student(); //
- // 保护Student,String 的"name" 字段
- AtomicReferenceFieldUpdater<Student,String> updater=AtomicReferenceFieldUpdater.newUpdater(Student.class,String.class,"name");
- // 再null的基础上更改
- updater.compareAndSet(student,null,"张三");
- updater.updateAndGet(student,a->"王五"); // 原子更新字段
- 状态
- RUNNING
- SHUTDWN 不会接接收新任务,但会处理阻塞队列剩余任务
- STOP 会中断正在执行的任务,并且会抛弃阻塞队列任务
- TIDYING 任务全执行完毕,祸端线程为0 即将进入终结
- TERYING 终结状态
- TERMINATED
- 构造方法
- 核心线程数(最多保留的线程数)
- 最大线程数目
- 生存时间--针对救急线程
- 时间单位--针对救急线程
- 阻塞队列
- 线程工厂
- 拒绝策略
-
- 当阻塞队列满了不能再放任务时候,会产生救急任务执行新任务
- 救急任务有生产时间
- 满负荷运行(核心线程满,救急线程满)才会执行拒绝策略
- 救急线程的前提是使用有界策略
AbortPolicy 默认策略,抛出 异常
CallerRunsPolicy让调用者运行任务
DiscarPolicy放弃本次任务
DiscarOldestPolicy 放弃队列中最早的任务,本次任务取而代之
newfFixedThreadPool(n)
核心线程数=最大线程数(没有救急线程)
例如
- ExecutorService pool = Executors.newFixedThreadPool(2); // 创建线程池
-
- pool.execute(()->{ //执行任务
- System.out.println(Thread.currentThread().getName()+" 1");
- });
- pool.execute(()->{//执行任务
- System.out.println(Thread.currentThread().getName()+" 2");
- });
- pool.execute(()->{//执行任务
- System.out.println(Thread.currentThread().getName()+" 3");
- });
newCachedThreadPool()
全部都是救急线程(60s后可以回收)
救急线程可以无限创建
阻塞容器
- SynchronusQueue<Integer> que=new SynchronusQueue<>();
- que.put(1) // put后其他线程调用 que.take()后才会继续向下执行,不然会一直阻塞
newSingThreadExecutor()
使用场景
希望多个任务队列排队执行。线程数固定为1,任务数多于1时,会放入无界队列排队。任务执行完毕,这唯一的线程也不会被释放,
即使发生异常,下一次也会有一个可用的线程,即每次都保证有一个可用的线程
- public static void test2(){
- ExecutorService pool=Executors.newSingleThreadExecutor();
- pool.execute(()->{
- System.out.println(Thread.currentThread().getName()+" 1");
- });
- pool.execute(()->{
- System.out.println(Thread.currentThread().getName()+(1/0));
- });
- pool.execute(()->{
- System.out.println(Thread.currentThread().getName()+" 3");
- });
- pool.execute(()->{
- System.out.println(Thread.currentThread().getName()+" 4");
- });
- pool.execute(()->{
- System.out.println(Thread.currentThread().getName()+" 5");
- });}
只有它是无返回值的,其他都是有返回值的
- public static void test2(){
- ExecutorService pool=Executors.newSingleThreadExecutor();
- pool.execute(()->{
- System.out.println(Thread.currentThread().getName()+" 1");
- });
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- ExecutorService pool = Executors.newFixedThreadPool(2); // 运行有返回值的
- Future<String> submit = pool.submit(() -> {
- Thread.sleep(1);
- System.out.println("run...");
- return "ok";
- });
- String s = submit.get(); // 没有结果就会阻塞
- System.out.println(s);
- }
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- ExecutorService pool = Executors.newFixedThreadPool(2); // 执行多个任务
- List<Future<Object>> futures = pool.invokeAll(Arrays.asList(
- () -> {
- System.out.println("任务1");
- return "1";
- },
- () -> {
- System.out.println("任务2");
- return "2";
- },
- () -> {
- System.out.println("任务3");
- return "3";
- }
-
- ));
- futures.forEach(f->{
- try {
- System.out.println(f.get()); // 获得返回结果 --会阻塞
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } catch (ExecutionException e) {
- throw new RuntimeException(e);
- }
- });
- }
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- ExecutorService pool = Executors.newFixedThreadPool(2);
- // 谁先运行结束,结果就是谁的返回值
- Object futures = pool.invokeAny(Arrays.asList(
- () -> {
- System.out.println("任务1");
- return "1";
- },
- () -> {
- System.out.println("任务2");
- return "2";
- },
- () -> {
- System.out.println("任务3");
- return "3";
- }
-
- ));
- System.out.println(futures);
- }
关闭线程池
- shutdown
- 线程次状态变成SHUTDOWN
- 不会接收新任务
- 但已提交任务会执行完
- 此方法不会阻塞线程的之执行
-
- shutdownNow
- 线程池状态变为STOP
- 不会接收新任务
- 会将队列中的任务返回
- 并用 interrupt 的方式中断正在执行的所有线程
-
- isShutdown()
- 不在RUNING 状态的线程池,此方法就会返回true方法
- isTerminated()
- 判断线程池状态是否是TERMINATED终结状态
-
- awaitTermination(long time,TimeUnit un)
- 线程池结束后需要做的事
例如:
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- ExecutorService pool = Executors.newFixedThreadPool(2);
- Future<Integer> submit = pool.submit(() -> {
- System.out.println("run1...");
- return 1;
- });
- Future<Integer> submit2 = pool.submit(() -> {
- System.out.println("run2...");
- return 2;
- });
- Future<Integer> submit3 = pool.submit(() -> {
- System.out.println("run3...");
- return 3;
- });
- System.out.println("shutdown");
- pool.shutdown(); // 不会阻塞线主线程的执行,线程池任务会继续执行
- pool.shutdown(); // 线程池任务会被打断}
定时任务
- public static void main(String[] args) {
- Timer timer=new Timer();
- TimerTask task=new TimerTask() {
- @Override
- public void run() {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- System.out.println("run1");
- }
- };
- TimerTask task2=new TimerTask() {
- @Override
- public void run() {
- System.out.println("run2");
- }
- };
- timer.schedule(task,1000); // 任务1的逻辑执行时长,会影响任务2 的执行
- timer.schedule(task2,1000);
- }
Timer的缺点:第一个任务抛出异常后,会使第二个任务无法执行,任务1的逻辑执行时长,会影响任务2 的执行
延时执行 schedule(代码,时间,时间单位)
- private static void Test(){
- // 任务调度线程池
- ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
- // 1秒后执行 无返回值类型的提交
- pool.schedule(()->{
- System.out.println("task2");
- },1, TimeUnit.SECONDS);
-
- // 第一个任务不会影响第二个任务
- pool.schedule(()->{
- System.out.println("task2");
- },1, TimeUnit.SECONDS);
- }
重复执行任务
- private static void Test(){
- // 任务调度线程池
- ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
- pool.scheduleAtFixedRate(()->{ // 时间间隔是 开始后执行
- System.out.println("执行");
- },1,1,TimeUnit.SECONDS); //代码逻辑 -- 初始延时-- 间隔时间--单位
- }
-
- pool.scheduleWithFixedDelay(()->{ // 时间间隔重复是 结束后再执行
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- System.out.println("执行");},1,1,TimeUnit.SECONDS);
- //代码逻辑 -- 初始延时-- 间隔时间--单位
- // 间隔时间是指上次执行完毕后再过多久重复运行
异常处理
手动trt
使用get() 如果有异常会捕获
用来进行线程同步协作,等待所有线程完成倒计时
await() 用来等待计数归零,countDown() 用来让计数减一
倒计时为0 时候等待线程就能继续运行
计数不能重新设置
例如
- public static void main(String[] args) throws InterruptedException {
- CountDownLatch latch=new CountDownLatch(3);
- new Thread(()->{
-
- try {
- TimeUnit.SECONDS.sleep(1);
- System.out.println("1开始...");
- latch.countDown();
- }
- catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }).start();
- new Thread(()->{
-
- try {
- TimeUnit.SECONDS.sleep(2);
- System.out.println("2开始...");
- latch.countDown();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }).start();
-
- new Thread(()->{
-
- try {
- TimeUnit.SECONDS.sleep(3);
- System.out.println("3开始...");
- latch.countDown();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }).start();
- latch.await(); // 阻塞 等于0 会运行下面
- System.out.println("运行");
-
- }
每次调用.await() 计数就会减一 并且阻塞当前线程当计数为0 时候await() 就会停止阻塞,计数为0时候再次调用,那么计数会再次变成初始值
例如
- static void test2(){
- ExecutorService executorService = Executors.newFixedThreadPool(2);
- CyclicBarrier barrier=new CyclicBarrier(2);
- for (int i=0;i<1000;i++){
- int j=i;
- executorService.submit(()->{
- System.out.println("运行"+j);
- try {
- TimeUnit.SECONDS.sleep(1);
- barrier.await(); // 2-1
- System.out.println(j+"结束");
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } catch (BrokenBarrierException e) {
- throw new RuntimeException(e);
- }
- });
- }
- executorService.shutdown();
- }
- 遗留的安全集合
- Hashtable
- Vector
- 修饰的安全集合(工具类调用,封装list或map)
- SynchronizedMap
- SychronizedList
- JUC安全集合
- Blocking 类型
- 大部分实现基于锁,并且供来使用来阻塞的方法
- CopyOnWrite类型
- 修改开销相对较重
- Concurrent类型
- 内部很多操作使用cas优化,一般可以提高吞吐量
- 弱一致性,容器被修改,遍历数据还是旧的
- size未必准确
- 其他非线程安全的集合在遍历时候发生修改,会快速失败
ConcurrentHashMap
- static void test4(){
- ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
- map.put(1,"张三"); // 单个操作是原子的,但是一群操作,不一定安全
- String s = map.get(1);
- System.out.println(s); //
- }
-
- map.computeIfAbsent(1,key->"value"); // 当 key 不存在时候 才会 put key-value 原子的
-
- static void test4(){
- ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
- // 当 key 不存在时候 才会 put key-value原子的 返回get(key)
- String s = map.computeIfAbsent(1, key -> "value");
- System.out.println(s);}
队列
LinkedBlockingQueue
ArrayBlockingQueue
ConcurrentLinkedQueue
CopyOnWriteArraySet