java 面试知识点笔记(十二)多线程与并发

Wesley13
• 阅读 596

问:synchronized和ReentrantLock的区别?

ReentrantLock(可重入锁)

  • 位于java.util.concurrent.locks包(著名的juc包是由Doug lea大神写的AQS抽象类框架衍生出来的应用)
  • 和CountDownLatch、FutureTask、Semaphore一样基于AQS实现
  • 能够实现比synchronized更细粒度的控制,如控制fairness
  • 调用lock()后,必须调用unlock()释放锁
  • 性能未必比synchronized高,并且也是可重入的

java 面试知识点笔记(十二)多线程与并发

ReentrantLock公平性设置

ReentrantLock fairLock = new ReentrantLock(true);

参数为ture时,倾向于将锁赋予等待时间最久的线程

公平锁:获取锁的顺序按先后调用lock方法的顺序(慎用,通常公平性没有想象的那么重要,java默认的调用策略很少会有饥饿情况的发生,与此同时若要保证公平性,会增加额外的开销,导致一定的吞吐量下降)

非公平锁:获取锁的顺序是无序的,synchronized是非公平锁

例子:

package interview.thread;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: cctv
 * @Date: 2019/5/21 11:46
 */
public class ReentrantLockDemo implements Runnable {

    private static ReentrantLock lock = new ReentrantLock(false);

    @Override
    public void run() {
        while (true) {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " get lock");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

    }

    public static void main(String[] args) {
        ReentrantLockDemo rtld = new ReentrantLockDemo();
        Thread t1 = new Thread(rtld);
        Thread t2 = new Thread(rtld);
        t1.start();
        t2.start();
    }
}

java 面试知识点笔记(十二)多线程与并发公平锁 new ReentrantLock(true);

java 面试知识点笔记(十二)多线程与并发非公平锁 new ReentrantLock(false);

ReentrantLock将锁对象化

  • 判断是否有线程,或者某个特定线程再排队等待获取锁
  • 带超时的获取锁尝试
  • 感知有没有成功获取锁

是否能将wait\notify\notifyAll对象化

  • java.util.concurrent.locks.Condition

总结synchronized和ReentrantLock的区别:

  1. synchronized是关键字,ReentrantLock是类
  2. ReentrantLock可以对获取锁的等待时间进行设置,避免死锁
  3. ReentrantLock可以获取各种锁信息
  4. ReentrantLock可以灵活的实现多路通知
  5. 机制:synchronized操作MarkWord,ReentrantLock调用Unsafe类的park()方法

java 面试知识点笔记(十二)多线程与并发

问:什么是Java内存模型中的happens-before?

java内存模型(即Java Memory Model 简称JMM)是一种抽象概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式

java 面试知识点笔记(十二)多线程与并发

JMM中的主内存

  1. 存储java实例对象
  2. 包括成员变量、类信息、常量、静态变量
  3. 属于数据共享区域,多线程并发操作时会引发线程安全问题

JMM中的工作内存

  1. 存储当前方法的所有本地变量信息,本地变量对其他线程不可见(工作内存是存储的主内存的变量的拷贝)
  2. 字节码行号指示器、native方法信息
  3. 属于线程私有的数据区域,不存在线程安全问题

JMM和java内存区域划分是不同的概念层次:

  • JMM描述的是一组规则,围绕原子性、有序性、可见性展开
  • 相似点:都存在共享区域和私有区域

主内存和工作内存的数据存储类型以及操作方式归纳:

  • 方法里的基本数据类型本地变量将直接存储在工作内存的栈帧结构中
  • 引用类型的本地变量:引用存储在工作内存中,实例存储在主内存中
  • 成员变量、static变量、类信息均会被存储在主内存中
  • 主内存共享的方式线程各拷贝一份数据到工作内存,操作完成后刷新回主内存

JMM如何解决可见性问题?

java 面试知识点笔记(十二)多线程与并发

首先要讲下重排序:在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。

指令重排序需要满足的条件:

  1. 单线程环境下不能改变程序运行的结果
  2. 存在数据依赖关系的不允许重排序(无法通过happens-before原则推导出来的,才能进行指令重排序)

happens-before的八大原则:

  1. 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作;
  2. 锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作;
  3. volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作;
  4. 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;
  5. 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;
  6. 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
  7. 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;
  8. 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;

如果两个操作不满足上述任意一个happens-before规则,那么这两个操作就没有顺序的保障,JVM可以对这两个操作进行重排序

如果操作A happens-before B 那么操作A在内存上所做的操作对操作B都是可见的

volatile:jvm提供的轻量级同步机制,解决了内存可见性问题,但并不是线程安全的(可以配合synchronized达到线程安全目的)

  • 保证被volatile修饰的共享变量对所有线程总是可见的
  • 禁止指令重排序优化

问:volatile变量如何立即可见?

当写一个volatile变量时,JMM会把该线程对应的工作内存中的共享变量刷新到主内存中

当读取一个volatile变量时,JMM会把该线程对应的工作内存置为无效,该线程只能从主内存中读取变量

问:volatile变量如何禁止重排优化?

内存屏障(Memory Barrier)

  1. 保证特定操作的执行顺序
  2. 保证某些变量的内存可见性

通过插入内存屏障指令禁止在内存屏障前后的指令执行重排序优化,强制刷出各种CPU的缓存数据,因此任何CPU上的线程都能读取到这些数据的最新版本

package interview.thread;

/**
 * 单例模式的双重检测实现
 *
 * @Author: cctv
 * @Date: 2019/5/21 17:19
 */
public class Singleton {
    // 禁止指令重排序优化
    private volatile static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        //第一次检测
        if (instance == null) {
            //同步
            synchronized (Singleton.class) {
                // 第二次检测
                if (instance == null) {
                    // 多线程环境下可能会出现问题的地方(会出现指令重排序,导致instance先赋值后初始化Singleton)
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

volatile和synchronized的区别

  • volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
  • volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
  • volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
  • volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
  • volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

问:谈谈 CAS(Compare and Swap)?

一种高效实现线程线程安全的方法

  • 支持原子更新操作,适用于计数器,序列发生器等场景
  • 属于乐观锁机制,号称lock-free(其实底层还是有加锁)
  • CAS操作失败时由开发者决定是否继续尝试,还是执行别的操作,所以失败线程不会阻塞挂起

cas思想:

包含三个操作数--内存位置V 预期原值A 和 新增B

  • 直接使用JUC的atomic包提供了常用的原子性数据类型以及引用、数组等相关原子类型和更新操作工具,是很多线程安全程序的首选
  • Unsafe类虽提供了CAS服务,但因为能够操纵任意内存地址的读写而有隐患
  • java9以后可以使用Variable Handle API来替代Unsafe

缺点:

  1. 若循环时间长,则开销很大
  2. 只能保证一个共享变量的原子操作
  3. ABA问题(解决方法:AtomicStampedReference,它可以控制变量的版本保证正确性)
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解2016年09月02日00:00:36 \牧野(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fme.csdn.net%2Fdcrmg) 阅读数:59593
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
5个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这