国内高端品牌网站建设,北京市建网站,wordpress不能外部链接,公司网站最下面突然有乱码JUC中创建的组件
JUC中创建的组件这些内容都不太常用#xff0c;偶尔用到面试的时候#xff0c;偶尔用到#xff01;到时候自行查找即可#xff0c;本文主要来快速的过一下#xff0c;留个印象即可~
JUC#xff08;java.util.concurrent#xff09;和多线程相关的工具…JUC中创建的组件
JUC中创建的组件这些内容都不太常用偶尔用到面试的时候偶尔用到到时候自行查找即可本文主要来快速的过一下留个印象即可~
JUCjava.util.concurrent和多线程相关的工具类。
1.Callable的用法
非常类似于Runnable描述了一个任务/一个线程要干啥Runnable通过run方法描述返回类型void但是很多时候是希望任务要有返回值的有一个具体的结果产出的 call的方法有返回值 写一个代码创建一个线程用这个线程计算一下1234……1000 import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class Main {public static void main(String[] args) throws ExecutionException,InterruptedException {//这只是创建个任务CallableInteger callablenew CallableInteger() {Overridepublic Integer call() throws Exception {//Integer 看返回值类型看实际需要int sum0;for (int i 1; i 1000; i) {sumsumi;}return sum;}};//还需要找个人来完成这个任务线程//Thread不能直接传Callable需要在包装一层FutureTaskInteger futureTasknew FutureTask(callable);Thread tnew Thread(futureTask);t.start();//取结果System.out.println(futureTask.get());}
}上述代码的运行结果为 那么在上述代码中如何保证调用get()的时候t线程的call方法是执行完毕了呢 其实get()和join()类似都是会阻塞等待~ 那么到目前为止我们有着以下四种方法来创建线程~
继承 Thread实现Runnable基于lambda实现Callable
ReentrantLock的可重入
synchronized关键字是基于代码块的方法来控制加锁解锁的
ReentrantLock则是提供了lock和unlock两个独立的方法来进行加锁解锁的虽然大部分情况下使用synchronized就足够了ReentrantLock也是一个重要的补充
体现在三个方面
synchronized只是加锁解锁加锁的时候如果发现锁被占用只能阻塞等待ReentrantLock还提供了一个trylock方法如果加锁成功没啥特殊的如果加锁失败不会阻塞直接返回false让程序员灵活的决定接下来做啥synchronized是一个非公平锁概率均等不遵循先来后到ReentrantLock提供了公平和非公平两种工作模式在构造方法中使true——》公平锁synchronized搭配wait()notify()进行等待唤醒如果多个线程wait()等待同一个对象notify()的时候是随机唤醒一个 ReentrantLock则是搭配Condition这个类这个类也能起到等待通知可以功能更加强大~ 信号量Semaphore
停车场外面的一个牌子显示剩余车位XXX这就用到信号量
信号量本质上是一个计数器描述了当前“可用资源的个数” P操作申请资源计数器-1V操作释放资源计数器1 如果计数器已经是0了继续申请资源就会阻塞等待~ 其实所谓的“锁”本质上是计数器为1的信号量 取值只有1和0两种也叫二元信号量 信号量是更广义的锁不光能管理非0既1的资源也能管理多个资源
JUC的一些线程安全集合类~
常用的有ArrayListLinkListHashMapPriorityQueue……线程不安全
如果多线程环境下使用就可能出现问题
最直接的方法使用锁来手动保证多个线程去修改Array List此时就可能有问题就可以给修改操作进行加锁操作标准库还提供了一些线程安全版本的集合类如果需要使用ArrayList则可以使用Vector代替不建议使用古老级别 CopyOnWeriteArrayList支持“写时拷贝”集合类 修改的时候就拷贝一份线程安全是多个线程修改不同变量没加锁 多线程使用“哈希表” HaspMap线程不安全肯定不行 HashTable线程安全的也是给关键方法加synchronized加到方法上相当于针对this加锁了 ConcurrentHashMap推荐方案
高频面试题HashTable和ConcurrentHashMap的区别面试高频
加锁粒度不同触发锁冲突的频率
HashTable是针对整个哈希表加锁任何的增删改查操作都会触发加锁也就都会可能有锁竞争实际上仔细思考其实没必要加锁这么勤快
HashTable的底层是一个数组链表 插入元素根据Key计算hash值——》数组下标把这个新的元素给挂到对应下标的链表上Java Hash Map还会再链表太长的时候把链表变为红黑树 如果咱们是两个线程插入元素 线程1插入的元素对应再下标为1的链表上~ 线程2插入的元素对应再下标为2的链表上~ 是否存在线程安全问题两个线程修改不同变量没啥事没有线程安全问题此时虽然两个操作没有线程安全问题但是由于synchronized是加到this上的仍然会针对同一个对象产生锁竞争产生阻塞等待 那么当不只有一把锁的时候 concurrentHashMap不是只有一把锁了是每个链表头节点作为一把锁
每次进行操作都是针对对应链表的锁进行加锁操作不同链表就是针对不同的锁进行加锁不会有竞争
该方法导致大部分加锁操作实际上没有锁冲突的此时这里的加锁操作的开销就微乎其微了
上述内容便是HashTable和ConcurrentHashMap的最大最核心区别
concurrentHashMap是把每个链表的头节点放到synchronized void put(String key,String value){//先找到对应链表的头节点int indexhashCode(key);Node headgetHead(index);synchronized (head){//执行链表插入节点的操作}}
上述的情况是从Java8开始的在Java1.7及其之前concurrentHashMap使用“分断锁”目的和上述是类似的相当于是好几个链表共用一把锁这个设定是不科学的效率不够高代码写起来也很麻烦…… 其他方面的改进
1.concurrentHashMap更重复的利用了CAS机制——》无锁编程
有的操作比如获取/更新元素个数就可以直接使用CAS完成不必加锁了CAS也能保证线程安全往往比锁更高效但是这个东西咱们也不会经常使用适用范围不像锁那么广泛
2.concurrentHashMap优化了扩容机制
HashTable如果元素过多就会涉及到扩容——》负载因子0.75扩容需要重新申请空间搬运元素把元素从旧的哈希表上删除插入到新的哈希表上如果元素本身非常多上亿个搬运一次成本就很高就会导致这一次的put()操作就会非常卡顿而concurrentHashMap策略——》化整为零并不会试图一次性的就把所有元素都搬运过去而是每次只搬运一小部分 当put()触发扩容此时就会直接创建更大的内存空间但是不会直接把所有元素都搬运过去而是只搬运一小部分这样的速度还是比较快的此时相当于存在两份hash表了此时插入元素直接往新表插入删除元素元素在哪个表的元素查找新表旧表都查看并且每次操作都搬运一部分过去 多线程比较复杂应用非常广泛的东西程序员必须要扎实掌握