多线程之哈希表、死锁等


前言

本文主要记录多线程中一些面试会提到的概念性问题。


记录一些锁:
乐观锁和悲观锁;
读写锁和普通互斥锁;
重量级锁和轻量级锁;
自旋锁和挂起等待锁;
公平锁和非公平锁;
可重入锁和不可重入锁。

一、cas和synchronized 的优化过程

1.CAS(compare and swap):比较并交换。
(1)CAS的执行过程:内存中的原数据V,旧的预期值A,需要修改的新值B;比较V和A是否相等;如果相等,则执行交换V=B,只关心V的值;返回操作是否成功。
(2)CAS的ABA问题:ABA问题就像是买一个手机,并不知道它是原装的还是翻新的。其解决方案是:给要修改的数据引入版本号。 在 CAS 比较数据当前值和旧值的同时, 也要比较版本号是否符合预期。如果发现当前版本号和之前读到的版本号一致, 就真正执行修改操作, 并让版本号自增; 如果发现当前版本号比之前读到的版本号大, 就认为操作失败。
(3)CAS的应用:CAS可以用来实现原子类和自旋锁。
2.synchronized是一种不公平锁、可重入锁、不是读写锁。
(1)加锁过程:JVM将其分为无锁、偏向锁、轻量级锁、重量级锁,会根据情况升级。
(2)优化操作:通过消除锁(编译器和JVM判断锁是否可以消除)和锁粗化(一段落即出现多次加锁,编译器JVM会自动进行锁粗化)。

二、synchronized 和 ReentrantLock的区别

1.synchronized是一个关键字,是JVM内部基于C++实现的;ReentrantLock是标准库中的一个内,在JVM外部基于Java实现的。
2.synchronized使用时不需要手动释放锁;ReentrantLock使用时需要手动释放,虽然使用起来更加灵活,但是容易遗漏unlock,可以使用try…finally。
3.synchronized在申请锁失败时会死等;ReentrantLock可以通过trylock的方式等待一段时间后放弃;
4.synchronized是非公平锁;ReentrantLock默认是非公平锁,可以通过构造方法传入true开启公平锁模式。

三、HashTable、HashMap和ConcurrentHashMap的区别

1.HashMap: 线程不安全。 key 允许为 null。
2.Hashtable: 线程安全.。使用 synchronized 锁 Hashtable 对象,效率较低(一个Hashtable只有一把锁,两个线程访问任意数据都会出现锁竞争)。key 不允许为 null。
3.ConcurrentHashMap: 线程安全。 使用 synchronized 锁每个链表头结点,锁冲突概率低,充分利用CAS 机制。优化了扩容方式.。key 不允许为 null。

四、死锁

1.死锁产生的四个必要条件
(1)互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
(2)不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
(3)请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
(4)循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
以上四个条件都成立时,就会产生死锁,而打破死锁则需破坏其中之一,由于(1)(2)是本质,而(3)是和写代码的方式有关,最容易的就是破坏(4)。
死锁代码示例:

public class Demo1 {
    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();
        Thread t1 = new Thread(()->{
            System.out.println("t1获取locker1");
            synchronized (locker1) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1获取locker2");
                synchronized (locker2) {
                    System.out.println("t1获取两把锁成功");
                }
            }
        });
        Thread t2 = new Thread(()->{
            System.out.println("t2获取locker2");
            synchronized (locker2) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2获取locker1");
                synchronized (locker1) {
                    System.out.println("t2获取两把锁成功");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

2.破坏循环等待
常用的方法则是锁排序,给锁进行编号,按照锁从小到大的顺序来获取锁,可以避免循环等待。

代码如下(示例):

public class Demo2 {
    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();
        Thread t1 = new Thread(()->{
            System.out.println("t1获取locker1");
            synchronized (locker1) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1获取locker2");
                synchronized (locker2) {
                    System.out.println("t1获取两把锁成功");
                }
            }
        });
        Thread t2 = new Thread(()->{
            System.out.println("t2获取locker1");
            synchronized (locker1) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2获取locker2");
                synchronized (locker2) {
                    System.out.println("t2获取两把锁成功");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值