Java多线程面试高配问题---线程并发安全(2)🧵

Java多线程

线程安全知识

1. synchronized关键字的底层原理


底层是一个monitor,在class文件中体现,加锁,解锁,为了防止代码出现bug,最后又解锁了一次,相当于一个try catch finally代码块。

Monitor实现
lock对象关联一个monitor

初级总结

2. synchronized关键字的底层原理-进阶

没有竞争的情况下可以用偏向锁与轻量级锁,一旦发生竞争,锁需要升级为重量级锁

monitor与lock对象是怎么关联上的?

要明白这些,首先要知道对象的内存结构。

对象头,实例数据,对齐填充(无意义的数据),为了保证字节是8的整数倍。
MarkWord-32bit

通过markword中的指针地址相关联

轻量级锁



偏向锁

偏向锁只有重入的第一次才做CAS操作,后面只是记录一下,判断上面的线程ID是不是自己

三种锁的对比

3. JMM(Java Memory Model)Java内存模型


通过主内存同步数据

4. CAS(Compare And Swap)


CAS流程

CAS底层实现
调用了操作系统的CAS指令,java 在 unsafe 类中,native 修饰的是操作系统底层的函数,由C或者C++实现的。

乐观锁与悲观锁

volatile关键字



代码运行之后,发现线程 3 还是一直在执行, 因为 jit 对我们的代码做了优化

volatile保证线程间的可见性

避免添加了volatile的变量被优化掉

volatile禁止指令重排序

示例:未加volatile

在y上添加volatile

在x上添加volatile,无效,因为写操作加的屏障只能阻止上面的指令不能往下走,不能阻止下边的指令往上走。
读操作的屏障,只能阻止下面的指令越过它排在它(添加了volatile的变量)之上。

volatile总结

5. 什么是AQS?(AbstractQueuedSynchronizer)

AQS本质也是一种锁 乐观锁

AQS基本工作机制

内部有一个state属性,有线程就来持有了锁,state变为1,其他线程再进来改state就失败了,加入到队列中,先进先出的双向链表,有头部和尾部。

假如同时来了两个线程0和线程4 同时抢这个锁

被0抢到了,线程4就加入队列,在队尾排队。

AQS是公平锁还是非公平锁?都可以实现****
此时线程0释放了锁,状态变为0

同时来了一个线程5,线程5和1同时抢,就是非公平锁

同时来了一个线程5,加入队尾,唤醒线程1持有锁,此时就是公平锁

公平锁与非公平锁

AQS总结

6. ReentrantLock的实现原理(可重入锁)

ReentrantLock基本介绍


可重入
可重入是指一个线程如果获取了锁,那么它就是锁的主人,那么它可以再次获取这把锁,这种就是理解为重入,简而言之,可以重复获取同一把锁,不会造成阻塞

实现原理


ReentrantLock总结

7. synchronized和Lock的区别?

三个层面:
1. 语法层面

2. 功能层面
可打断:在等待获取锁的过程中,可以被其他线程打断,可以通过调用线程的interrupt()提前终止线程。举个例子:

public void testInterrupt() throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();

        // 主线程普通加锁
        System.out.println("主线程优先获取锁");
        lock.lock();
        try {
            // 创建子线程
            Thread t1 = new Thread(() -> {
                try {
                    System.out.println("t1尝试获取打断锁");
                    lock.lockInterruptibly();
                } catch (InterruptedException e) {
                    System.out.println("t1没有获取到锁,被打断,直接返回");
                    return;
                }
                try {
                    System.out.println("t1成功获取锁");
                } finally {
                    System.out.println("t1释放锁");
                    lock.unlock();
                }
            }, "t1");
            t1.start();
            Thread.sleep(2000);
            System.out.println("主线程进行打断锁");
            t1.interrupt();
        } finally {
            // 主线程解锁
            System.out.println("主线程优先释放锁");
            lock.unlock();
        }
    }

可超时:调用tryLock(long timeout, TimeUnit unit)方法,在给定时间内获取锁,获取不到就退出,这也是synchronized没有的功能。

 public void testLockTimeout() throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            try {
                // 调用tryLock获取锁
                if (!lock.tryLock(2, TimeUnit.SECONDS)) {
                    System.out.println("t1获取不到锁");
                    return;
                }
            } catch (InterruptedException e) {
                System.out.println("t1被打断,获取不到锁");
                return;
            }
            try {
                System.out.println("t1获取到锁");
            } finally {
                lock.unlock();
            }
        }, "t1");
        // 主线程加锁
        lock.lock();
        System.out.println("主线程获取到锁");

        t1.start();
        Thread.sleep(3000);
        try {
            System.out.println("主线程释放了锁");
        } finally {
            lock.unlock();
        }
    }

多条件变量:可以对锁设置条件,达到了条件要求后才能获取锁.

在线程1中调用c1.await();,线程2中调用c2.await(),线程3中按顺序唤醒c1,c2,调用c1.signal(),c2.signal()
如下示例




3. 性能层面

8. 死锁产生的条件是什么?

四个必要条件
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

什么是死锁

当两个线程为了保护两个不同的共享资源而使用了两个互斥锁,那么这两个互斥锁应用不当的时候,可能会造成两个线程都在等待对方释放锁,
在没有外力的作用下,这些线程会一直相互等待,就没办法继续运行,这种情况就是发生了死锁。




9. ConcurrentHasMap

1.7

segment数组不能扩容,且每次只能有一个线程拿到锁,性能比较低

1.8

10. java程序中怎么保证多线程的执行安全(导致并发程序出现问题的根本原因)?

Java 并发编程的三大特性

  • 原子性
  • 可见性
  • 有序性





synchronized保证原子性,valitale保证可见性和有序性

posted @   xiaolifc  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示