JUC 类(ConcurrentHashMap ,CopyOnWriteArrayList,CopyOnWriteArraySet ,辅助类)ReentrantLock和Synchronized

JUC 类

hashMap是线程不安全的不能并发操作

hashTable是线程安全的有synchronized修饰,将锁直接加到put()上,效率低,相当于吧整个hash表锁住了,用在低并发情况下可以,独占锁

ConcurrentHashMap

ConcurrentHashMap 是线程安全的,采用锁分段机制,并没有将整个hash表锁住,但是jdk8之后没有使用分段锁(给每个位置创建一个锁标志对象),采用的是CAS思想+synchronized来实现

插入时检测hash表对应的位置是否是第一个节点,如果是,采用CAS机制(循环检测)向第一个位置插入数据

如果此位置已经有值,那么就以第一个Node对象为锁标志进行加锁,使用的是synchronized实现

public class HashMapDemo {
    
    

    /*
       HashMap是线程不安全的,不能并发操作的
       ConcurrentModificationException  并发修改异常   遍历集合,并删除集合中的数据

       Hashtable 是线程安全的 public synchronized V put(K key, V value)-->独占锁
            锁直接加到了put方法上,锁粒度比较大,效率比较低
            用在低并发情况下可以

       Map<String,Integer> map = Collections.synchronizedMap(new HashMap<>());
       ConcurrentHashMap
     */
    public static void main(String[] args) {
    
    

        ConcurrentHashMap<String,Integer> map = new ConcurrentHashMap<>();
        //模拟多个线程对其操作
        for (int i = 0; i < 20; i++) {
    
    
                 new Thread(
                     ()->{
    
    
                       map.put(Thread.currentThread().getName(), new Random().nextInt());
                         System.out.println(map);
                     }
                 ).start();
        }

    }
}

CopyOnWriteArrayList

读写完全分离 读操作完全不用加锁,读不影响数据

写操作加锁,写操作不影响读操作 两个线程同时添加会互斥

添加时先将原数组复制出一个副本

然后就将数据添加到副本中,不影响读操作

最后用副本替换原数组

CopyOnWriteArraySet

是一个不允许重复数据的

底层实现是CopyOnWriteArrayList,不能存储重复数据

辅助类CountDownLatch

使一个线程等待其他线程执行结束后再执行

相当于一个递减的线程计数器

先制定一个数量,当有一个线程结束后就减一,直到为0 关闭计数器 这样线程就执行了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wS5P0aFG-1642418593327)(C:\Users\云\AppData\Roaming\Typora\typora-user-images\1642401770485.png)]

辅助类 CyclicBarrier

让一组线程达到一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门

是一个加法计数器,当线程数量达到指定数量时才会开门放行

java中的锁

很多锁的名词,这些分类并不是全是指锁,有的指锁的特性,有的指锁的设计,

有的指锁的状态,下面总结的内容是对每个锁的名词进行一定的解释。

乐观锁/悲观锁

乐观锁:就是不加锁,认为并发的修改是没有问题的,例如CAS机制 设计一种无锁方式实现.适合读操作多

悲观锁: 认为并发操作会出现问题,需要通过加锁来保证安全.适合写操作多

可重入锁

Reentrant Lock

可以一定程度上避免死锁

当一个同步方法中,调用另一个和他使用同一把锁的方法时

在外层方法中即使没有释放的情况下,也可以进入到另一个同步方法

Reentrant Lock和synchronized都是可重入锁

eg:setA()和setB()方法用的是同一把锁,在进入setA方法后拿到了锁当调用setB方法,如果不是可重入锁,在setA()方法没有释放锁,setB()方法就不会被当前线程执行,当如果是可重入锁,setB()方法就可以顺利执行,不会造成死锁情况.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p1ee1woE-1642418593330)(C:\Users\云\AppData\Roaming\Typora\typora-user-images\1642417471732.png)]

读写锁(ReadWriteLock)

是具体的锁实现,读和写是两把锁,进行分离使用

分段锁

是一种加锁思想,并不是实际的锁实现,采用分段加锁,降低锁的粒度,从而提高效率

自旋锁(SpinLock)

不是一种锁实现,采用自旋(循环重试) 的方式进行尝试,获取执行权,不会让线程进入到阻塞的状态,适用于锁的时间较短的情况

CAS就是基于自旋锁实现

共享锁

可以被多个线程共享的锁

ReadWriteLock的读锁是共享的,多个线程可以同时读数据

独占锁

也称互斥锁,一次只能有一个线程获取锁

Reentrant Lock,synchronized,ReadWriteLock的写锁是独占锁

ReadWriteLock里面实现方式使用了同步对列,例如读线程获取资源,将标准state设置为已被使用,然后将其他的写线程加入到一个对列中等待.

AQS(AbstractQueuedSynchronizer)

维护一个对列,让等待的线程排队.

公平锁

就是会维护一个线程的等待对列,一次去执行线程

Reentrant Lock默认是非公平的,可以在创建时通过构造方法为其制定是公平锁还是非公平锁

非公平锁

没有对列,一旦锁释放,线程开始抢占,谁抢到执行权,谁先执行

锁的状态

无锁/偏向锁/轻量级锁/重量级锁

偏向锁:指的是一直只有一个线程在不断的获取锁,可以更方便的获取到锁

轻量级锁:当锁是偏向锁的时候,被另一个线程访问,偏向锁就会升级为轻量级锁,如果是轻量级锁那么等待线程不会进入阻塞状态,采用自旋方式,重新尝试获取锁,效率提高

重量级锁:当锁状态为轻量级锁时,如果有的线程自旋次数过多,或者有大量的线程访问,那么锁状态升级为重量级锁,此时未获得锁的线程不再自旋,进入阻塞状态

ReentrantLock

是类,只能修饰代码块 是显示锁,手动添加 手动释放

是类层面实现控制,采用CAS+AQS 是可重入锁,可以是公平锁,也可以不是公平锁

在类的内部维护了一个锁的状态,一旦有线程抢占到了,将状态改为1,其他的线程就进入到对列中等待锁的释放(当为公平锁时),锁一旦释放,那么就唤醒头结点,开始尝试去获得锁

synchronized

是可重入锁,非公平锁

是一个关键字,可以修饰代码块,也可以修饰方法

是隐式锁,可以自动获取释放锁

synchronized 实现加锁释放锁是指令级别的

有一个进入监视器进入+1 对象头锁标记被使用

执行任务

退出监视器 -1 当等于0时 对象头锁标记改为无锁

猜你喜欢

转载自blog.csdn.net/crraxx/article/details/122546568
今日推荐