JUC基础知识(个人总结)

    声明: 1. 本文为我的个人复习总结, 并那种从零基础开始普及知识 内容详细全面, 言辞官方的文章
              2. 由于是个人总结, 所以用最精简的话语来写文章
              3. 若有错误不当之处, 请指出

一. 前置基础:

  • IO 操作不占用 cpu, 只是我们一般拷贝文件使用的是【阻塞 IO】, 这时相当于线程虽然不用 cpu, 但需要一

    直等待 IO 结束, 没能充分利用线程。

    所以才有后面的【非阻塞 IO】和【异步 IO】优化

  • synchronized同步 是互斥的

  • 锁的本质是 大家对一个共享资源进行互斥访问

  • 栈是线程私有, 堆是多线程共享的;

    但不意为着线程栈里的局部变量就被别的线程访问到, 需要先进行逃逸分析;

    比如将局部变量传递给方法内部的开辟的线程, 那样此变量就逃逸出此方法了, 便会有线程安全问题

  • 进程 是对运行时程序的封装,操作系统进行资源调度资源分配的基本单位

    线程 是cpu进行调度的基本单位

    管程 是监视器, 即synchronized锁

    举例: 一个正在运行的Java程序是一个JVM进程, 其中既有用户线程, 又有GC垃圾回收线程(守护线程); 可见一个进程里可以有多个线程

  • 无论是主线程还是自定义线程,只有有存活JVM就不会结束;

    而当仅有守护线程时,JVM便会结束

  • 锁粒度不能太大, 但又不能太小; 既要考虑并发能力, 又要考虑安全性

查看进程线程的方法

Windows

tasklist 查看进程

taskkill 杀死进程

Linux

ps -Tf -p <PID> 查看某个进程(PID)的所有线程

top -H -p <PID> 查看某个进程(PID)的所有线程

Java

jstack <PID> 查看某个 Java 进程(PID)的所有线程状态

栈帧:

每个栈由多个栈帧(Frame)组成, 对应着每次方法调用时所占用的内存

每个线程只能有一个活动栈帧, 对应着当前正在执行的那个方法

栈帧内包括: 局部变量, 返回地址, 操作数栈, args参数, 锁记录

多线程是为了充分利用cpu资源, 不会因为单个的阻塞而阻塞整个业务流程。

多线程串行 相比于 单线程 的优势:

​ 多线程串行, 即便有个线程阻塞了, 也不会影响别的线程;

​ 而且当未对临界区进行修改发生竞态条件时, 是不需要加锁的, 这时优势就又更明显了

同步/异步, 是从 多个线程间的 业务逻辑关系或回调通知机制 的角度来看的

串行/并行, 是从 多个线程间的 实际执行过程 的角度来看的

异步业务: 商品详情功能业务 & 评论功能业务
同步业务: 登录功能业务 & 购物车功能业务

一般而言, 由于业务是异步的关系, 导致了并行执行; 由于业务是同步的关系, 导致了串行执行

同步:

  1. 两个事物保持某种相对关系
  2. syncronized互斥
  3. 消息通知机制上 没有回调
  4. 业务同步, 有先后执行顺序

join( ): 插队, 即等此线程结束

join(3000L) 最多让插队3s, 超时main就不等t1了,t1走t1的, main继续往下走

// t1花费1s, t2花费2s, 总花费并不是3s, 而是2s; t1和t2可以看作并行运行
public static main(String[] args){
    Thread t1 = new Thread(( ) -> {
        sleep(1);
        r1 = 10;
    });
    Thread t2 = new Thread(( ) -> {
        sleep(2);
        r2 = 20;
    });
    t1.start( );
    t2.start( );
    t1.join( );	//是main线程调用,故插队到main前
    t2.join( );
}		

yield( ): 礼让一下

LockSupport.park( ) LockSupport.unpark( );

可以先调用unpark, 那么下一次park就会无效; 注意先调用的unpark只能累加一次, 不能叠加

操作系统层面的线程状态:

在这里插入图片描述

在这里插入图片描述

JVM层面的线程状态:

在这里插入图片描述

Java层面的 RUNNABLE 状态涵盖了操作系统层面的【可运行状态】、【运行状态】和【阻塞状态】(BIO导致的线程阻塞, 在Java里无法区分, 仍然认为是可运行)

Java层面的阻塞状态有: BLOCKED(没抢到锁), WAITING(wait), TIMED_WAITING(带时间的wait)

线程通信的几种方式:

  • 共享内存(全局变量)
  • synchronized锁
  • wait( )/notify( )
  • join( )
  • 并发工具: CountDownLatch, CyclicBarrier
  • 管道
  • 信号量

二. synchronized底层:

synchronized底层是Monitor, 翻译为监视器, 重量级锁或管程

Monitor底层结构:

  1. Owner 抢到锁的线程
  2. EntryList 没抢到锁而阻塞的线程
  3. WaitSet wait的线程, 相当于一间休息室; 所以wait必须放在synchronized中执行

synchronized的字节码:

在这里插入图片描述

第4行的astore是存放锁的引用, 因为后面需要找到锁进行释放

monitorenter后有两个monitorexit, 第一个monitorexit是正常执行时的释放锁, 第二个monitorexit是发生异常时进行释放锁

锁的相关信息(线程id, 是否为偏向锁, 锁重入的次数)放在对象头的MarkWord里存储

JDK6中引入了偏向锁&轻量级锁, 有了锁升级的概念

1. 偏向锁:

轻量级锁在没有竞争时(就自己一个线程), 每次重入仍然需要执行CAS操作

而偏向锁: 只有第一次使用CAS时将线程ID设置到对象的Mark Word头, 之后发现这个线程ID是自己的 就表示没有竞争, 不用重新CAS

偏向锁可以重偏向到其他线程

撤销偏向锁:

​ 即升级为轻量级锁, 需要STW

如果撤销偏向到达某个阈值, 那么整个类的所有对象都会变为不可偏向的

2. 轻量级锁:

多个线程错开访问, 没有竞争时 可以使用轻量级锁来优化

3. 重量级锁:

多线程间有竞争

synchronized锁的状态只能升级不能降级

其他锁的概念:

自旋锁: 抢锁失败后先别着急阻塞, 而是多循环尝试几次; 因为线程切换 阻塞状态和可运行状态 是需要代价的

​ 是使用CAS来实现的

锁膨胀: 即锁升级, 偏向锁->轻量级锁->重量级锁

锁粗化: 连续好几个synchronized同一个锁对象, 且中间未夹杂其他代码, 则被粗化成一个synchronized代码块

锁消除: 逃逸分析后发现局部变量没有逃逸出此方法, 便会对此局部变量加的锁消除掉

死锁: 两个线程互相把此对方的锁, 且都不愿意先释放自己的锁, 造成一种互相等待的僵局

一般发生在synchronized的代码块里又嵌套一个synchronized代码块

死锁发生的条件:

   1. 互相把持对方的锁, 且都不愿意先释放已有的锁
   2. 对于对方的锁, 非获取不可
   3. 没有外界干扰打断

详细版:

  1. 互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放
  2. 请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放
  3. 不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用
  4. 循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。

解决方案:

   1. 主动放弃锁:  使用if+tryLock, 不是非获取锁不可, 能获得就获得返回true然后执行,获得不了返回false就算了就不执行了
   2. 被动放弃锁:  外界干预打断, 调用interrupt方法  

活锁: 两个线程执行相反的操作, 比如A线程对初始值为10的cnt进行++操作, B线程对cnt进行–操作;

​ 两个线程一直在运行, cnt的值却很久都达到不了0, 结束不了运行

公平锁: 所有线程雨露均沾, 防止有些线程太强了干完了所有活, 导致其他线程饥饿着没活干;

​ 但这个会降低并发, 所以不建议使用;

​ new ReentrantLock(true); 是公平锁, 默认为false

可重入锁: 在锁代码块内部重复获得相同的锁, 又称递归锁

三. 多线程安全问题:

多线程安全问题: 多个线程对共享的一块内存进行写操作, 即临界区发生了竞态条件

一段代码块内如果存在对共享资源的多线程读写操作, 称这段代码块为临界区

多个线程在临界区内执行, 由于代码的执行序列不同而导致结果无法预测, 称之为发生了竞态条件

虚假唤醒: if 里线程被wait打断后再醒来时可能数据已经变了, 而却不重复判断

1. 原子性:

  • i++的原子性问题: 最初cnt=0, A线程++得到1,还没的及写入却被阻塞了,B又对0–成-1写入, 这时A继续写为1; 导致了0 ++ --=1

  • 多个操作间的原子性问题: 线程切换导致if判断不准确。比如10000元去买东西, if(10000>8000), if(10000>4000), if(10000>2000), 因为线程被打断来不及扣减, 最终10000-8000-4000-2000<0 负债了

解决方案:

  1. 阻塞式的解决方案:synchronized, AQS

  2. 非阻塞式的解决方案:原子类(即CAS+volatile)

2. 可见性:

JIT编译将热点代码缓存到工作内存里, 那样工作内存里存储的就不是主内存里的最新数据

解决方案:

  1. volatile, 保证可见性和有序性
  2. 有synchronized即可禁止JIT编译将此部分代码缓存到工作内存, 无论锁谁都行
  3. 全局禁用JIT(不推荐)

3. 有序性:

cpu调整指令顺序

比如一会加载a, 一会加载b, 循环1000次; cpu发现单线程下不影响结果的前提下, 肯定优先先加载1000次a, 再去加载1000次b

int a = 0;    
boolean flag = false;    
public void writer( ){    
    a = 1;                 
    flag = true;   
}    
	
public void reader( ){    
    if(flag){       
	int i = a + a; 
    }    
}

解决方案:

​ 加锁, synchronized或AQS

综上:

  • volatile能解决 可见性 & 有序性 问题

  • 乐观锁CAS 能解决 原子性 & 可见性 & 有序性 问题

  • 悲观锁synchronized或AQS 能解决原子性 & 可见性 & 有序性问题

  • 用private和final修饰方法,可以避免一些问题, 因为如果不然的话, 在子类中, 创建了新线程让新的线程共享list局部变量, 就坑爹了

  • as-if-serial规则保证 单线程下的指令重排, 不会影响执行结果

    happens-before规则保证 多线程下的指令重排, 不会影响执行结果

happens-before规则:

规定了哪些写操作对其它线程的读操作可见, 它是可见性有序性的一套规则总结

一个操作happens-before另一个操作,表示第一个的操作结果对第二个操作可见

  1. 线程解锁 m 之前对变量的写, 对于接下来对 m 加锁的其它线程对该变量的读可见
  2. 线程对 volatile 变量的写, 对接下来其它线程对该变量的读可见
  3. 线程 start 前对变量的写, 对该线程开始后对该变量的读可见
  4. 线程结束前对变量的写, 对其它线程得知它结束后的读可见(比如其它线程调用 t1.isAlive( )t1.join( )等待它结束)
  5. 线程 t1 打断 t2(interrupt)前对变量的写, 对于其他线程得知 t2 被打断后对变量的读可见(通 过t2.interruptedt2.isInterrupted)
  6. 对变量默认值(0, false, null)的写, 对其它线程对该变量的读可见
  7. 具有传递性, 如果 x -> y 并且 y -> z 那么有 x -> z

volatile底层原理:

是更轻量级的锁

可见性: 被volatile修饰的变量执行写操作时: 立即将工作内存中对此变量的数据刷写到主内存中, 并使得其他线程工作内存中对此变量的缓存失效

有序性: volatile修饰的共享变量在被读写时会 加入内存屏障,阻止其他读写操作越过屏障

​ volatile 读写加入的屏障只能防止同一线程内的指令重排

JMM内存模型对volatile的排序规则为:

  • 当第一个操作是volatile读时,无论第二个操作是什么都不能进行重排序

  • 当第二个操作是volatile写时,无论第一个操作是什么都不能进行重排序

  • 当第一个操作是volatile写,第二个操作为volatile读时,不能进行重排序

JMM(Java内存模型)

JMM 定义了一套保障数据的可见性有序性原子性的规则;

架构:

  1. 工作内存 线程私有
  2. 主内存 线程公有的

内存屏障:

Load是读, Store是写

  1. LoadLoad

    Load1, LoadLoad, Load2; 即先Load1, 再Load2

  2. StoreStore

    Store1, StoreStore, Store2; 即先Store1, 再Store2

  3. LoadStore

    Load1, LoadLoad, Store2; 即先Load1, 再Store2

  4. StoreLoad

    Store1, StoreStore, Load2; 即先Store1, 再Load2

转账案例:

public class ExerciseTransfer {
    public static void main(String[] args) throws InterruptedException {
        Account a = new Account(1000);
        Account b = new Account(1000);
        Thread t1 = new Thread(( ) -> {
            for (int i = 0; i < 1000; i++) {
                a.transfer(b, randomAmount( ));
            }
        }, "t1");
        Thread t2 = new Thread(( ) -> {
            for (int i = 0; i < 1000; i++) {
                b.transfer(a, randomAmount( ));
            }
        }, "t2");
        t1.start( );
        t2.start( );
        t1.join( );
        t2.join( );
        log.debug("total:{}", (a.getMoney( ) + b.getMoney( )));
    }

    static Random random = new Random( );

    public static int randomAmount( ) {
        return random.nextInt(100) + 1;
    }
}

class Account {
    private int money;

    public Account(int money) {
        this.money = money;
    }

    public int getMoney( ) {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public void transfer(Account target, int amount) {
        // 注意, 不能锁this, 因为上文一个线程 a.transfer(b, randomAmount( ));, 另一个线程 b.transfer(a, randomAmount( ));
        // 当this是a时, 线程二的b根本不理会this(a)这把锁
        synchronized (Account.class) {
            if (this.money > amount) {
                this.setMoney(this.getMoney( ) - amount);
                target.setMoney(target.getMoney( ) + amount);
            }
        }
    }
}

四. CAS与原子类:

CAS:

CAS(compare and set/swap)是乐观锁, 本质并不是锁, 而是while循环进行版本号判断

悲观锁 VS 乐观锁:

悲观锁靠阻塞实现, 乐观锁使用循环代替阻塞

悲观锁: 适用于锁竞争激烈的场景(读少写多)

​ 优点: 线程抢不到锁 阻塞时会让出cpu

​ 缺点: 线程切换状态, 有一定的开销

乐观锁: 适用于锁竞争不激烈(读多写少), 且cpu核数较多的场景

​ 优点: 线程不用切换状态

​ 缺点: 线程就一直执行while占用cpu

  • CAS 是基于乐观锁的思想: 不怕别的线程来修改共享变量,就算改了也没关系,我吃亏点再重试呗
  • Synchronized 是基于悲观锁的思想: 防着其它线程来修改共享变量,我上了锁你们都别想改,我改完了解开锁,你们才有机会

原子类:

底层使用CAS+volatile实现无锁并发

JUC包中的4种原子类:

  • 基本类型

    更新基本类型

    • AtomicInteger:整形原子类
    • AtomicLong:长整型原子类
    • AtomicBoolean:布尔型原子类
  • 数组类型

    更新数组里的某个元素

    • AtomicIntegerArray:整形数组原子类
    • AtomicLongArray:长整形数组原子类
    • AtomicReferenceArray:引用类型数组原子类
  • 引用类型

    • AtomicReference
    • AtomicStampedReference:
    • AtomicMarkableReference:
  • 更新类的字段

    需要配合volatile使用, volatile修饰该字段

    • AtomicIntegerFieldUpdater: 整型字段的更新器
    • AtomicLongFieldUpdater: 长整型字段的更新器。
    • AtomicReferenceFieldUpdater:引用类型的更新器

ABA问题:

值A变成了B后又变回了A, 这两个A认不认为它是同一个版本呢?

​ AtomicReference和AtomicMarkableReference认为是(即存在ABA问题), AtomicStampedReference认为不是

​ 解决: 使用版本号

LongAdder:

LongAdder的累加效率要比AtomicLong高:

LongAdder 分为 base 和 cells 两部分:

  1. 没有并发竞争的时候或者是 cells 数组正在初始化的时候,会使用 CAS 来累加到base
  2. 有并发竞争时,会初始化 cells 数组,数组有多少个 cell,就允许有多少线程并行修改; 最后将数组中每个 cell 累加,再加上 base 就是最终的值

相当于降低了锁的粒度, 只锁cells累加单元, 而不是锁住所有

UnSafe:

这个类很底层, 不建议我们直接使用

像CAS和NIO的直接内存, 都是通过底层调用UnSafe来实现的

五. 常见线程安全类:

String

Integer

StringBuffer

Random

Vector

Hashtable

java.util.concurrent 包下的类

这里的安全指的是 多个线程操作同一个实例时不会带来线程安全问题

String和Integer都是不可变类, 所谓的修改不过是new一个新的对象返回

对于Hashtable, 单个API是原子性的,但是组合起来不能保证其原子性,

如下图: 原本是想着table为null才put值, 结果因为API组合起来不具备原子性, 从而put了两次

在这里插入图片描述

六. ReentrantLock:

特点:

  1. 支持多个Condition条件变量, 即具有多个WaitSet休息室, 可以只叫醒某一个休息室的线程

    await signal signalall

  2. 可以使用tryLock

  3. 得手动释放锁

  4. 可重入

  5. 可以设置超时时间

  6. 可以设置为公平锁

  7. 可中断

ReentrantReadWriteLock:

读锁是共享锁, 写锁是排他锁, 写锁可以降级为读锁

七. Callable, Future:

Callable:

// 有返回值; 且抛出了异常,子类不必非得try catch了
class MyThread implements Callable<Integer> {
    @Override
    public Integer call( ) throws Exception {
        return 200;
    }
}

Future:

  • 在主线程中需要执行比较耗时的操作时但又不想阻塞主线程时,可以把这些作业交给Future在后台完成,

    当主线程将来需要时,就可以通过Future的get方法获得结果

  • 将Future视为保存结果的对象, 它暂时不保存结果, 但将来会保存 (即Callable返回的时候), 为了获得结果,得需要Callable

  • 类的关系: class FutureTask<V> implements RunnableFuture<V>
    interface RunnableFuture<V> extends Runnable, Future<V>

相关API:

FutureTask<Long> futureTask2 = new FutureTask(callable);

  1. public boolean cancel(boolean flag) //用于停止任务
    • 如果尚未启动,它将停止任务
    • 如果已经启动, 当flag为true时, 才会强行停止任务
  2. public Object get( ) //用于获取任务的结果, 只计算一次
    • 如果任务完成,它将立即返回结果
    • 否则将阻塞等待任务完成,然后返回结果; 所以一般get方法放到最后
  3. public boolean isDone ( ) //判断任务是否完成

CompletableFuture:

是Future的实现类, 用作编排多个线程间的关系, 有异步回调功能

八. Fork/Join:

Fork:把一个复杂任务进行拆分, 递归拆分

Join:把拆分任务的结果进行合并

案例: 计算 1+2+3…+1000, 拆分成子任务, 每个子任务做100个数字的累加

class TaskExample extends RecursiveTask<Long> {
    private int start;
    private int end;
    private long sum;

    public TaskExample(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute( ) {
        System.out.println("任务" + start + "=========" + end + "累加开始");
        //大于 100 个数相加切分,小于直接加
        if (end - start <= 100) {
            for (int i = start; i <= end; i++) {
                //累加
                sum += i;
            }
        } else {
            //切分为 2 块
            int middle = start + 100;
            //递归调用,切分为 2 个小任务
            TaskExample taskExample1 = new TaskExample(start, middle);
            TaskExample taskExample2 = new TaskExample(middle + 1, end);
            //执行:异步
            taskExample1.fork( );
            taskExample2.fork( );
            //同步阻塞获取执行结果
            sum = taskExample1.join( ) + taskExample2.join( );
        }
        //加完返回
        return sum;
    }
}

public class ForkJoinPoolDemo {

    public static void main(String[] args) {
        //定义任务
        TaskExample taskExample = new TaskExample(1, 1000);
        //定义执行对象
        ForkJoinPool forkJoinPool = new ForkJoinPool( );
        //加入任务执行
        ForkJoinTask<Long> result = forkJoinPool.submit(taskExample);
        //输出结果
        try {
            System.out.println(result.get( ));
        } catch (Exception e) {
            e.printStackTrace( );
        } finally {
            forkJoinPool.shutdown( );
        }
    }
}

九. 线程池:

为什么使用线程池:

  1. 复用Thread对象, 防止频繁地创建和销毁, 从而降低资源消耗 并提高响应速度

  2. 提高线程的可管理性, 控制其上限

七大参数:

  1. corePoolSize 核心线程数目
  2. maximumPoolSize 最大线程数目
  3. keepAliveTime 救急线程的生存时间
  4. unit 时间单位 救急线程的生存时间单位
  5. workQueue 等待队列
  6. threadFactory 线程工厂, 可以定制线程对象的创建如 设置线程名字、是否是守护线程等
  7. handler 拒绝策略
    1. 抛异常 AbortPolicy
    2. 返回给调用者去执行 CallerRunsPolicy
    3. 丢弃此任务 DiscardPolicy
    4. 丢弃最早排队的任务 DiscardOldestPolicy

救急线程优先处理新来的任务, 而不是呆在等待队列里的任务

创建线程池的几种方法:

  1. 使用Executors工厂方法创建

    这里的无限大是说为int最大值的意思

    1. newSingleThreadExecutor: 单线程
    2. newFixedThreadPool: 固定大小但等待队列无限大
    3. newCachedThreadPool: 有救急线程但线程数无限大的线程池
    4. newScheduledThreadPool: 线程数无限大, 且支持定时调度
  2. 自己通过new ThreadPoolExecutor 方法传参创建

线程的销毁:

救急线程在空闲一定时间后自动销毁

核心线程可调用shutdown等方法销毁

  1. shutdown

    • 不接收新任务

    • 已提交任务会执行完

    • 不会阻塞调用此方法的线程

    • 线程池状态变为 SHUTDOWN

  2. shutdownNow

    • 不接收新任务

    • 已提交但还在队列里没开始执行的任务会被取消

    • 用interrupt打断正在执行的任务

    • 线程池状态变为 STOP

十. AQS常用的锁:

AQS: AbstractQueuedSynchronizer 抽象队列同步器

ReentrantLock, FutureTask, CountDownLatch, CyclicBarrier, Semaphore 都是基于AQS实现的

AQS可以独占, 也可以共享

AQS内部有一些CAS代码

AQS的原理:

AQS维护了一个共享资源, 然后使用双向队列来保证线程排队获取资源

用 volatile state 字段 来表示同步状态, 通过CAS进行setState

队列里只有头节点才是锁的持有者,尾指针指向队列的最后一个等待线程节点

在这里插入图片描述

AQS的资源共享方式有哪些?

通过修改state字段来实现

  • Exclusive:独占, 只有一个线程可以执行, 例如ReentrantLock
  • Share:共享, 多个线程可同时执行, 如ReentrantReadWriteLock

在这里插入图片描述

在这里插入图片描述

AQS使用了模板方法设计模式

自定义AQS:

  1. 继承AbstractQueuedSynchronizer

  2. 重写方法:

    1. isHeldExclusively( ):判断该线程是否正在独占资源

    2. tryAcquire(int):独占方式, 尝试获取资源

    3. tryRelease(int):独占方式, 尝试释放资源

    4. tryAcquireShared(int):共享方式, 尝试获取资源; 负数表示失败, 0表示无可用资源, 正数有可用资源

    5. tryReleaseShared(int):共享方式, 尝试释放资源

CountDownLatch(递减计数器):

当计数器的值变为0时,await方法阻塞的线程会被唤醒继续执行

案例: 6个同学陆续离开教室后值班同学才可以关门

public class CountDownLatchDemo {

    public static void main(String[] args) throws Exception {
        //定义一个数值为 6 的计数器
        CountDownLatch countDownLatch = new CountDownLatch(6);
        //创建 6 个同学(线程)
        for (int i = 1; i <= 6; i++) {
            new Thread(( ) -> {
                try {
                    if (Thread.currentThread( ).getName( ).equals("同学 6")) {
                        Thread.sleep(2000);
                    }
                    System.out.println(Thread.currentThread( ).getName( ) + "离开了");
                    //计数器减一,不会阻塞
                    countDownLatch.countDown( );
                } catch (Exception e) {
                    e.printStackTrace( );
                }
            }, "同学" + i).start( );
        }
        countDownLatch.await( ); // 当计数器为0时,才被唤醒
        System.out.println("全部离开了,现在的计数器为" + countDownLatch.getCount( ));
    }
}

CyclicBarrier(循环栅栏):

每执行cyclicBarrier.await( )一次, 障碍数就会加一, 如果达到了目标障碍数, 才会执行

构造器: public CyclicBarrier(int parties, Runnable runable)

案例: 经历81难才能取得真经

public class CyclicBarrierDemo {

    private final static int NUMBER = 81;

    public static void main(String[] args) throws Exception{
        //定义循环栅栏, 定义的run方法(lambda表达式), await( )81次才会被执行
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, ( ) -> System.out.println("集齐" + NUMBER + "颗龙珠,现在召唤神龙!!!!!!!!!"));
        //定义 7 个线程分别去收集龙珠
        for (int i = 1; i <= NUMBER; i++) {
                try {
                    System.out.println("经历第" + i+"难");
                    Thread.sleep(500L)
                    cyclicBarrier.await( );
                } catch (Exception e) {
                    e.printStackTrace( );
                }
        }
    }
}

Semaphore(信号灯):

定义最大许可证, 每个信号量最多只同时有一个许可证。

使用 acquire 方法获得许可证,release 方法释放许可证

案例: 6辆汽车(线程)抢3个停车位

public class SemaphoreDemo {

    public static void main(String[] args) throws Exception {
        //定义 3 个停车位
        Semaphore semaphore = new Semaphore(3);
        //模拟 6 辆汽车停车
        for (int i = 1; i <= 6; i++) {
            Thread.sleep(100);
            //停车
            new Thread(( ) -> {
                try {
                    System.out.println(Thread.currentThread( ).getName( ) + "找车位 ing");
                    semaphore.acquire( );
                    System.out.println(Thread.currentThread( ).getName( ) + "汽车停车成功!");
                    Thread.sleep(10000L); //停车中
                } catch (Exception e) {
                    e.printStackTrace( );
                } finally {
                    System.out.println(Thread.currentThread( ).getName( ) + "溜了溜了");
                    semaphore.release( );
                }
            }, "汽车" + i).start( );
        }
    }
}

邮戳锁StampedLock:

是对读写锁的优化, 乐观读, 类似于CAS, 戳即版本号

  1. 乐观读:

    long stamp = lock.tryOptimisticRead( );

    // 验戳

    if(!lock.validate(stamp)){

    // 锁升级

    }

  2. 读锁:

    long stamp = lock.readLock( );

    lock.unlockRead(stamp);

  3. 写锁:

    long stamp = lock.writeLock( );

    lock.unlockWrite(stamp);

十一. 大对比:

wait VS sleep:

  1. wait必须放在synchronized 里配合Monitor一起使用, 而sleep不必
  2. wait是Object里的方法, 而sleep是Thread里的静态方法
  3. wait会释放锁, 而sleep不会释放锁

wait VS park:

  1. wait, notify必须放在synchronized 里配合Monitor一起使用, 而park, unpark不必

  2. wait是Object里的方法, 而park是LockSuport里的方法

  3. notify只能随机唤醒一个等待线程, notifyAll是唤醒所有等待线程, 不是那么精确;

    而park, unpark 是以线程为单位阻塞唤醒线程, 比较精确

  4. wait & notify 不能先 notify,而park & unpark 可以先 unpark

  5. wait会释放锁, 而park不会释放锁

打断 sleep, wait, join, park 的线程:

  1. interrupt( ) // 打断线程
    • 打断正在 sleep, wait, join的线程, 会抛出InterruptedException, 并清除打断标记
    • 打断的正在运行或park的线程, 则会设置打断标记
  2. isInterrupted( ) // 判断线程是否被打断, 不会清除打断标记
  3. static interrupted( ) // 判断线程是否被打断, 会清除打断标记

synchronized VS volatile:

  1. synchronized 底层靠的是Monitor, 保证了 原子性, 可见性, 有序性 操作较重

    volatile 底层是靠禁用JIT编译将数据缓存到工作内存 保证可见性, 靠读写内存屏障保证有序性, 而对于原子性却无能为力; 操作较轻

  2. synchronized作用于代码块或方法, volatile作用于变量

  3. synchronized会造成线程的阻塞, volatile不会造成线程的阻塞

synchronized VS Lock:

  1. synchronized 的底层是Monitor 是靠底层C++实现的, 而Lock是靠Java实现的

  2. synchronized 会自动释放锁, 而Lock需要手动释放锁(放在finally里)

  3. Lock有更丰富的功能, 比如:

    • tryLock
    • Condition(相当于多个WaitSet, 可以唤醒指定WaitSet里的线程)
    • 读写锁(使锁粒度更细, 读读共享)
  4. 性能方面:

    • 在没有竞争时,synchronized 做了很多优化, 如偏向锁、轻量级锁; 性能不错

    • 在竞争激烈时,Lock性能更好(比如读共享)

Runable VS Thread:

  1. Runable是接口, Thread是类, 且Runable是Thread的父接口
  2. Runable的线程启动, 需要由Thread去代理启动, 将Runable实现类作为参数传给Thread构造器
  3. 由于Java的单继承多接口, 所以优先使用Runable

Runable run( ) VS Callable call( ):

  1. run无返回值, 而call有返回值, 因此可以结合Future来使用
  2. run方法自身没有抛出异常, 因此子类只能try catch, 而call方法自身抛出了异常, 子类不必非得try catch

execute VS submit:

execute( )方法用于提交不需要返回值的任务, 而submit( )方法用于提交需要返回值的任务

所以: execute( )方法只能执行Runnable 类型的任务, 而submit( )方法可以执行RunnableCallable类型的任务

死锁 VS 活锁:

死锁是两个线程相互等待, 处于阻塞状态

活锁并未阻塞, 一直在动着, 在不断循环尝试, 只是条件一直不满足

十二. 代码案例:

线程八锁问题:

  1.  import java.util.concurrent.TimeUnit;
     
     //先byPlane, 后byCar 其实同时锁住了两个方法, 但没有时间暂停, 看不出来
     public class Sync_1 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_A vehicle1 = new Vehicle_A( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //只是为了排除线程先后启动的误差, 使认为线程1比线程2先启动
             new Thread(( ) -> vehicle1.byCar( )).start( );
         }
     
     }
     
     class Vehicle_A {
     
         public synchronized void byPlane( ) {
             System.out.println("------byPlane------");
         }
     
         public synchronized void byCar( ) {
             System.out.println("------byCar------");
         }
     
     }
    
  2.  import java.util.concurrent.TimeUnit;
     
     //3s后 先byPlane, 后byCar     说明同时锁住了两个方法
     public class Sync_2 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_B vehicle1 = new Vehicle_B( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //只是为了排除线程先后启动的误差
             new Thread(( ) -> vehicle1.byCar( )).start( );
         }
     
     }
     
     class Vehicle_B {
     
         public synchronized void byPlane( ) {
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException ignored) {
             }
             System.out.println("------byPlane------");
         }
     
         public synchronized void byCar( ) {
             System.out.println("------byCar------");
         }
     
     }
    
  3.  import java.util.concurrent.TimeUnit;
     
     //先hello, 3s后再byPlane     说明没有同时锁住两个方法
     public class Sync_3 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_C vehicle1 = new Vehicle_C( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //只是为了排除线程先后启动的误差
             new Thread(( ) -> vehicle1.hello( )).start( );
         }
     
     }
     
     class Vehicle_C {
     
         public synchronized void byPlane( ) {
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException ignored) {
             }
             System.out.println("------byPlane------");
         }
     
         public synchronized void byCar( ) {
             System.out.println("------byCar------");
         }
     
         public void hello( ) {
             System.out.println("------hello------");
         }
     
     }
    
  4.  import java.util.concurrent.TimeUnit;
     
     //先byCar, 3s后再byPlane     说明没有同时锁住两个方法
     public class Sync_4 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_D vehicle1 = new Vehicle_D( );
             Vehicle_D vehicle2 = new Vehicle_D( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //只是为了排除线程先后启动的误差
             new Thread(( ) -> vehicle2.byCar( )).start( );
         }
     
     }
     
     class Vehicle_D {
     
         public synchronized void byPlane( ) {
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException ignored) {
             }
             System.out.println("------byPlane------");
         }
     
         public synchronized void byCar( ) {
             System.out.println("------byCar------");
         }
     
     }
    
  5.  import java.util.concurrent.TimeUnit;
     
     //3s后 先byPlane, 后byCar     说明同时锁住了两个方法
     public class Sync_5 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_E vehicle1 = new Vehicle_E( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //只是为了排除线程先后启动的误差
             new Thread(( ) -> vehicle1.byCar( )).start( );
         }
     
     }
     
     class Vehicle_E {
     
         public synchronized static void byPlane( ) {
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException ignored) {
             }
             System.out.println("------byPlane------");
         }
     
         public synchronized static void byCar( ) {
             System.out.println("------byCar------");
         }
     
     }
    
  6.  import java.util.concurrent.TimeUnit;
     
     //3s后 先byPlane, 后byCar     说明同时锁住了两个方法
     public class Sync_6 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_F vehicle1 = new Vehicle_F( );
             Vehicle_F vehicle2 = new Vehicle_F( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //只是为了排除线程先后启动的误差
             new Thread(( ) -> vehicle2.byCar( )).start( );
         }
     
     }
     
     class Vehicle_F {
     
         public synchronized static void byPlane( ) {
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException ignored) {
             }
             System.out.println("------byPlane------");
         }
     
         public synchronized static void byCar( ) {
             System.out.println("------byCar------");
         }
     
     }
    
  7.  import java.util.concurrent.TimeUnit;
     
     //先byCar, 3s后再byPlane     说明没有同时锁住两个方法
     public class Sync_7 {
     
         public static void main(String[] args) throws InterruptedException {
             Vehicle_G vehicle1 = new Vehicle_G( );
     
             new Thread(( ) -> vehicle1.byPlane( )).start( );
             TimeUnit.MILLISECONDS.sleep(500L);  //只是为了排除线程先后启动的误差
             new Thread(( ) -> vehicle1.byCar( )).start( );
         }
     
     }
     
     class Vehicle_G {
     
         public synchronized static void byPlane( ) {
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException ignored) {
             }
             System.out.println("------byPlane------");
         }
     
         public static void byCar( ) {
             System.out.println("------byCar------");
         }
     
     }
    
  8.  import java.util.concurrent.TimeUnit;//先byCar, 3s后再byPlane    说明没有同时锁住两个方法public class Sync_8 {    public static void main(String[] args) throws InterruptedException {        Vehicle_H vehicle1 = new Vehicle_H( );        Vehicle_H vehicle2 = new Vehicle_H( );        new Thread(( ) -> vehicle1.byPlane( )).start( );        TimeUnit.MILLISECONDS.sleep(500L);  //只是为了排除线程先后启动的误差        new Thread(( ) -> vehicle2.byCar( )).start( );    }}class Vehicle_H {    public synchronized static void byPlane( ) {        try {            TimeUnit.SECONDS.sleep(3);        } catch (InterruptedException ignored) {        }        System.out.println("------byPlane------");    }    public synchronized void byCar( ) {        System.out.println("------byCar------");    }}
    

总结:

  1. 锁非静态方法为锁this,锁静态方法为锁对应的类.class

  2. 锁住静态时不影响非静态方法, 但影响其他的静态

  3. 锁住非静态时不影响静态, 但影响其他的非静态

多个线程交替打印问题:

  1. 三个线程T1、T2、T3交替打印A、B、C, 打印10次, 如ABCABCABCABC…

  2. 三个线程T1、T2、T3交替打印A(5次)、B(10次)、C(15次), 打印10次, 如AAAAABBBBBBBBBBCCCCCCCCCCCCCCCAAAAABBBBBBBBBBCCCCCCCCCCCCCCCAAAAABBBBBBBBBBCCCCCCCCCCCCCCCAAAAABBBBBBBBBBCCCCCCCCCCCCCCCAAAAABBBBBBBBBBCCCCCCCCCCCCCCCAAAAABBBBBBBBBBCCCCCCCCCCCCCCCAAAAABBBBBBBBBBCCCCCCCCCCCCCCCAAAAABBBBBBBBBBCCCCCCCCCCCCCCCAAAAABBBBBBBBBBCCCCCCCCCCCCCCCAAAAABBBBBBBBBBCCCCCCCCCCCCCCC…

  3. 三个线程交替打印1-100, T1打印1, T2打印2, T3打印3, T1打印4…

  4. 两个线程交替打印字母和数字, T1打印A1, T2打印B2, T1打印C3…一直到Z26, 如A1B2C3…

synchronized & wait & notify版解法:

  1.   public class Demo01 {
      
          private static Object lock = new Object( );
          private static int num;
      
          public static void printABC(String show, int targetNum) throws InterruptedException {
              for (int i = 0; i < 10; i++) {
                  synchronized (lock) {
                      while (num % 3 != targetNum) {
                          lock.wait( );
                      }
                      num++;
                      System.out.print(show);
                      lock.notifyAll( );
                  }
              }
          }
      }
      
      class Test01 {
      
          public static void main(String[] args) {
              new Thread(( ) -> {
                  try {
                      Demo01.printABC("A", 0);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
              new Thread(( ) -> {
                  try {
                      Demo01.printABC("B", 1);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
              new Thread(( ) -> {
                  try {
                      Demo01.printABC("C", 2);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
          }
      }
    
  2.  public class Demo02 {
     
     
         private static Object lock = new Object( );
         private static int num;
     
         public static void printABC(String show, int targetNum) throws InterruptedException {
             for (int i = 0; i < 10; i++) {
                 synchronized (lock) {
                     while (num % 3 != targetNum) {
                         lock.wait( );
                     }
                     num++;
                     for (int j = 0; j < 5 * (targetNum + 1); j++) {
                         System.out.print(show);
                     }
                     lock.notifyAll( );
                 }
             }
         }
     }
     
     class Test02 {
     
         public static void main(String[] args) {
             new Thread(( ) -> {
                 try {
                     Demo02.printABC("A", 0);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo02.printABC("B", 1);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo02.printABC("C", 2);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
         }
     }
    
  3.  public class Demo03 {
     
         private static Object lock = new Object( );
         private static int num;
     
         public static void print_1_100(String threadName, int targetNum) throws InterruptedException {
             // num [0,99] num++后再打印num
             while (true) {
                 synchronized (lock) {
                     while (num % 3 != targetNum) {
                         lock.wait( );
                     }
                     num++;
                     if (num > 100) {
                         // 不叫醒其他线程的话,会导致其他线程都在wait  程序不会结束
                         lock.notifyAll( );
                         break;
                     }
                     System.out.println(threadName + ":" + num);
                     lock.notifyAll( );
                 }
             }
         }
     }
     
     class Test03 {
     
         public static void main(String[] args) {
             new Thread(( ) -> {
                 try {
                     Demo03.print_1_100("T1", 0);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo03.print_1_100("T2", 1);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo03.print_1_100("T3", 2);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
         }
     }
    
  4.  public class Demo04 {
          public static Object lock = new Object( );
          public static int num;
      
          public static void print(int order) throws InterruptedException {
              while (true) {
                  synchronized (lock) {
                      while (num % 2 != order) {
                          lock.wait( );
                      }
                      num++;
                      if (num > 26) {
                          lock.notifyAll( );
                          break;
                      }
                      System.out.print((char) (num - 1 + 'A'));
                      System.out.print(num);
                      lock.notifyAll( );
                  }
              }
          }
      }
      
      class Test04 {
      
          public static void main(String[] args) {
              new Thread(( ) -> {
                  try {
                      Demo04.print(0);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
              new Thread(( ) -> {
                  try {
                      Demo04.print(1);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
          }
      }
        
    

Lock & Condition版解法:

  1.  public class Demo01 {
     
         private static int num;
     
         public static void printABC(String show, int targetNum, Lock lock, Condition cur, Condition next) throws InterruptedException {
             for (int i = 0; i < 10; i++) {
                 lock.lock( );
                 try {
                     while (num % 3 != targetNum) {
                         cur.await( );
                     }
                     num++;
                     System.out.print(show);
                     next.signal( );
                 } finally {
                     lock.unlock( );
                 }
             }
         }
     }
     
     class Test01 {
         private static ReentrantLock lock = new ReentrantLock( );
         private static Condition condition1 = lock.newCondition( );
         private static Condition condition2 = lock.newCondition( );
         private static Condition condition3 = lock.newCondition( );
     
         public static void main(String[] args) {
             new Thread(( ) -> {
                 try {
                     Demo01.printABC("A", 0, lock, condition1, condition2);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo01.printABC("B", 1, lock, condition2, condition3);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo01.printABC("C", 2, lock, condition3, condition1);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
         }
     }
    
  2.  public class Demo02 {
     
         private static int num;
     
         public static void printABC(String show, int targetNum, Lock lock, Condition cur, Condition next) throws InterruptedException {
             for (int i = 0; i < 10; i++) {
                 lock.lock( );
                 try {
                     while (num % 3 != targetNum) {
                         cur.await( );
                     }
                     num++;
                     for (int j = 0; j < 5 * (targetNum + 1); j++) {
                         System.out.print(show);
                     }
                     next.signal( );
                 } finally {
                     lock.unlock( );
                 }
             }
         }
     }
     
     class Test02 {
         private static ReentrantLock lock = new ReentrantLock( );
         private static Condition condition1 = lock.newCondition( );
         private static Condition condition2 = lock.newCondition( );
         private static Condition condition3 = lock.newCondition( );
     
         public static void main(String[] args) {
             new Thread(( ) -> {
                 try {
                     Demo02.printABC("A", 0, lock, condition1, condition2);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo02.printABC("B", 1, lock, condition2, condition3);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo02.printABC("C", 2, lock, condition3, condition1);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
         }
     }
    
  3.  public class Demo03 {
     
         private static int num;
     
         public static void print_1_100(String threadName, int targetNum, Lock lock, Condition cur, Condition next) throws InterruptedException {
             // num [0,99] num++后再打印num
             while (true) {
                 lock.lock( );
                 try {
                     while (num % 3 != targetNum) {
                         cur.await( );
                     }
                     num++;
                     if (num > 100) {
                         // 不叫醒其他线程的话,会导致其他线程都在wait  程序不会结束
                         next.signal( );
                         break;
                     }
                     System.out.println(threadName + ":" + num);
                     next.signal( );
                 } finally {
                     lock.unlock( );
                 }
             }
         }
     }
     
     class Test03 {
         private static ReentrantLock lock = new ReentrantLock( );
         private static Condition condition1 = lock.newCondition( );
         private static Condition condition2 = lock.newCondition( );
         private static Condition condition3 = lock.newCondition( );
     
         public static void main(String[] args) {
             new Thread(( ) -> {
                 try {
                     Demo03.print_1_100("A", 0, lock, condition1, condition2);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo03.print_1_100("B", 1, lock, condition2, condition3);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
             new Thread(( ) -> {
                 try {
                     Demo03.print_1_100("C", 2, lock, condition3, condition1);
                 } catch (InterruptedException e) {
                     e.printStackTrace( );
                 }
             }).start( );
         }
     }
    
  4.  public class Demo04 {
          public static int num;
      
          public static void print(int order, Lock lock, Condition cur, Condition next) throws InterruptedException {
              while (true) {
                  lock.lock( );
                  try {
                      while (num % 2 != order) {
                          cur.await( );
                      }
                      num++;
                      if (num > 26) {
                          next.signal( );
                          break;
                      }
                      System.out.print((char) (num - 1 + 'A'));
                      System.out.print(num);
                      next.signal( );
                  } finally {
                      lock.unlock( );
                  }
              }
          }
      }
      
      class Test04 {
          private static ReentrantLock lock = new ReentrantLock( );
          private static Condition condition1 = lock.newCondition( );
          private static Condition condition2 = lock.newCondition( );
      
          public static void main(String[] args) {
              new Thread(( ) -> {
                  try {
                      Demo04.print(0, lock, condition1, condition2);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
              new Thread(( ) -> {
                  try {
                      Demo04.print(1, lock, condition2, condition1);
                  } catch (InterruptedException e) {
                      e.printStackTrace( );
                  }
              }).start( );
          }
      }  
    
  • 7
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java JUCJava Util Concurrent)是Java平台的一个并发编程库,提供了一些并发编程的工具和框架。以下是Java JUC的一些重要知识点: 1. Lock接口和ReentrantLock类:提供了一种比Java中的synchronized关键字更灵活、可定制化的同步机制。 2. Condition接口:可以和Lock接口一起使用,提供了一种等待通知机制,可以让线程在等待某个条件成立时挂起,直到被其他线程唤醒。 3. Semaphore类:提供了一种信号量机制,可以限制某些资源的并发访问量,保证程序的稳定性。 4. CountDownLatch类:提供了一种倒计时锁机制,可以让某个线程在其他线程都完成后再执行。 5. CyclicBarrier类:提供了一种栅栏机制,可以让多个线程在某个点上进行同步,等待所有线程都到达后再同时执行。 6. Executor框架:提供了一种线程池机制,可以更好地管理线程,提高程序的性能和稳定性。 7. CompletableFuture类:提供了一种异步编程机制,可以让程序在等待某些操作的同时继续执行其他操作,提高程序的并发性能。 这些都是Java JUC的重要知识点,掌握它们可以帮助开发者更好地编写高并发、高性能的程序。 ### 回答2: Java JUCJava Util Concurrency)是Java并发编程的工具类库,提供了一些多线程编程的辅助工具和数据结构,主要包括锁、原子变量、并发容器、线程池等。 首先,Java JUC提供了多种类型的锁,如ReentrantLock、ReadWriteLock等。这些锁可以用来控制对共享资源的访问,保证线程的安全性。通过使用锁,可以实现线程的互斥访问和公平竞争访问,防止资源的并发访问导致的数据不一致的问题。 另外,Java JUC还提供了一些原子变量,比如AtomicInteger、AtomicLong等。原子变量是线程安全的,可以保证对其操作的原子性。通过使用原子变量,可以避免多线程环境下对共享变量的竞争导致的数据错乱问题。 并发容器也是Java JUC的重要组成部分,如ConcurrentHashMap、ConcurrentLinkedQueue等。这些并发容器是线程安全的,可以在多线程环境下安全地处理数据。通过使用并发容器,可以提高多线程程序的性能和并发访问的效率。 最后,Java JUC还提供了线程池的支持,通过线程池可以实现线程的复用、统一管理和调度。线程池可以减少线程的创建和销毁的开销,并且可以控制并发线程的数量,避免因为线程数过多导致系统资源耗尽的问题。 总之,Java JUC的知识点涵盖了锁、原子变量、并发容器和线程池等多个方面,可以帮助程序员更好地进行多线程编程,提高程序的性能和并发访问的效率。 ### 回答3: Java JUCjava.util.concurrent)是Java中用于处理多线程并发编程的工具包。它提供了一套强大的并发编程工具和类,帮助开发者更加方便地编写高效、稳定的多线程程序。 Java JUC包含了以下几个重要的知识点: 1. 锁机制:Java JUC提供了多种类型的锁机制,包括ReentrantLock、StampedLock等,用于实现线程同步和互斥访问共享资源。通过使用锁机制,可以确保多个线程之间的数据一致性和线程安全性。 2. 阻塞队列:Java JUC提供了多种类型的阻塞队列,如ArrayBlockingQueue、LinkedBlockingQueue等。阻塞队列是一种特殊的队列,当队列为空或者已满时,插入和删除操作会被阻塞,直到满足条件后再继续执行。 3. 线程池:Java JUC中的线程池机制可以重用线程,减少线程的创建和销毁开销,提高系统的性能和资源利用率。通过ThreadPoolExecutor类,可以方便地创建和管理线程池,并根据实际需求调整线程池的大小和线程池中线程的执行方式。 4. 原子操作:Java JUC提供了一系列原子类,如AtomicInteger、AtomicLong等,用于支持对共享变量进行原子操作,以避免线程竞争和数据不一致的问题。原子类提供了一系列原子性的方法,保证了多线程环境下的安全访问。 5. 并发容器:Java JUC提供了一些线程安全的并发容器,如ConcurrentHashMap、CopyOnWriteArrayList等,用于在多线程环境下安全地处理数据结构。这些并发容器支持高并发读写操作,提供更好的性能和可伸缩性。 总之,Java JUC提供了一组强大的并发编程工具和类,能够帮助开发者更好地处理多线程编程中的并发性和线程安全性问题。通过熟练掌握和应用这些知识点,可以编写出高效、稳定的多线程程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值