大厂必问的JUC知识

面试题

谈谈你对volatile的理解

可见性

java内存模型规定所有变量存储到主内存,多个线程操作属性时 会从主内存中拷贝一份到自己的工作内存空间,其中某一个线程修改完写会主内存 其他线程是不知道的,让其他线程知道主内存的属性改变就叫可见性

class Person{
  volatile   int age =0;
    public void show(){
        this.age=60;
    }
}
public class Demo {
    public static void main(String[] args) {
        Person p = new Person();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"come in"+p.age);
            try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}
            p.show();
            System.out.println(Thread.currentThread().getName()+"ok"+p.age);
        },"AAA").start();
        while(p.age==0){
           // System.out.println("循环");
        }
        System.out.println(Thread.currentThread().getName()+"执行完毕"+p.age);
    }
}

不保证原子性

import java.util.concurrent.atomic.AtomicInteger;

class Person{
    int age =0;
    AtomicInteger atomicInteger = new AtomicInteger();
    public void show(){
        age++;
    }
    public void show1(){
        atomicInteger.getAndIncrement();
    }
}
public class Demo {
    public static void main(String[] args) {
        Person p = new Person();
        for (int i = 0; i < 1000; i++) {
            new Thread(()->{
                for (int j = 0; j <2000 ; j++) {
                    p.show();
                }
            },i+"").start();
        }
        for (int i = 0; i < 1000; i++) {
            new Thread(()->{
                for (int j = 0; j <2000 ; j++) {
                    p.show1();
                }
            },i+"").start();
        }

        while(Thread.activeCount()>2){
           Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"执行完毕"+p.age);
        System.out.println(Thread.currentThread().getName()+"执行完毕"+p.atomicInteger);

    }
}

上面的案例 :因为age++由三步组成 发现volatile 无法保证原子性 使用synchronized大材小用,所以加入了AtomicInteger 来保证操作的整数是原子性的 那么他为什么可以保证原子性 后面CAS说

禁止指令重排

cpu中加入屏障 屏障前后不能重排序

class Person{
    int age =0;
    boolean flag=false;
    public void show(){
        this.age=1; //语句1
        this.flag=true; //语句2         这俩条语句如果发生重拍 结果会不一样
        System.out.println(age+"show");
    }
    public void show1(){
        if(this.flag){
            age=age+5;//5 6
            System.out.println(age+"show1");
        }
    }
}

你在哪些地方用到了volatile

单例模式

以懒汉式的单例模式举例 ,会有并发问题存在 可以加synchronized 解决 但是并发量会下降

double check lock

class Person{
    private  Person(){
        System.out.println("构造被调用");
    }
    private static Person p = null;
    public  static Person getInstance() {
        if (p == null) {
            synchronized (Person.class) {
                if(p==null) {
                    p = new Person();
                }
            }
        }
        return p;
    }
}
public class Demo {
    public static void main(String[] args) {
        for (int i = 0; i <10 ; i++) {
            new Thread(()->{
                Person.getInstance();
            }).start();
        }

    }
}

这个程序正确率99.99999% 但为什么还是出错

因为 p=new Person 不存在数据依赖 可能会发生指令重排

分三步 1内存开辟空间 2变量p指向内存空间 3初始化对象 重排之后 1 3 2 线程A执行完 3 此时对象创建好但是p还是为空 B线程过来 又创建了一个对象 这样就会发生多个对象的情况

在这里插入图片描述

CAS你知道么

比较并交换

多个线程操作的时候 先把主内存的值读取到各自的工作内存 每个线程都会设置期望值 就是在写回的时候比较期望值和开始主内存的值是否一样 如果不一样说明被人修改过 就要重新读取主内存的值

import java.util.concurrent.atomic.AtomicInteger;

public class Demo {
    public static void main(String[] args) {

        AtomicInteger atomicInteger = new AtomicInteger(5);
        System.out.println(atomicInteger.compareAndSet(5, 2019));//true
        System.out.println(atomicInteger.compareAndSet(5, 2020));//flse


    }
}

说一说CAS原理

为什么 atomicInteger.getAndIncrement();能够保证原子性 地层以来了unsafe类

unsafe底层用c语言调用内存地址

在这里插入图片描述

再回头有看CAS 期望值就是指定内存的值

源码

在这里插入图片描述

CAS缺点

循环时间长 开销大 只能保证一个功效变量的原子操作 ABA问题

ABA问题

T1线程执行得慢 取出来的值是A 期望值是A B线程 执行的快 取出A 改成B 又改成C 最后又改成A,中间的一系列操作之后 T1写回 发现和期望值一样 对于T1来说 没人动过这个数据 单其实被该国很多次了

原子引用类型

也就是自定义一个原子类

在这里插入图片描述

原子版本号解决

类 AtomicStampReference

加入版本号 每修改一次版本号加一次

AtomicStampReference.compareAndSet(期望值 ,设置值,期望版本号 ,设置版本号)

AtomicStampReference.getStamp() 获取版本号

AtomicStampReference.getReference() 获取值

公平锁与非公平锁

Lock lock = new Reentrantlock() 默认是非公平锁 传true就是公平锁

对于synchroized锁来说也是非公平锁

公平锁 多个线程按照申请顺序来获取锁

非公平锁:多个线程获取锁的顺序和申请顺序无关,有可能造成饥饿现象

可重入锁(递归锁)

同步方法A调用同步方法B 会自动获取B的锁

synchroized和Reentrantlock是可重入锁 避免死锁 ,

自旋锁

不适用锁 而是使用循环达到锁的目的

import java.util.concurrent.atomic.AtomicReference;

class MyThread{
    AtomicReference<Thread> reference = new AtomicReference<>();
    Thread t = Thread.currentThread();
    public void mylock(){
        System.out.println(Thread.currentThread().getName()+"进入");
        while (!reference.compareAndSet(null,t)){
            System.out.println(Thread.currentThread().getName()+"循环");
        }
        System.out.println(Thread.currentThread().getName()+"完成");
    }
    public void myUnLock(){
        reference.compareAndSet(t,null);
        System.out.println(Thread.currentThread().getName()+"释放锁");
    }
}
public class Demo {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        new Thread(()->{
            myThread.mylock();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myThread.myUnLock();
        },"AA").start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            myThread.mylock();
            myThread.myUnLock();
        },"BB").start();


    }
}

独占锁(写锁)共享锁(读锁)互斥锁

读写锁

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Demo{
    public static void main(String[] args)   {
        Book book = new Book();
        for (int i = 0; i <5 ; i++) {
            String a = String.valueOf(i);
              new Thread(()->{
                  book.put(a,a);
              },String.valueOf(i)).start();
        }
        for (int i = 0; i <5 ; i++) {
            String b = String.valueOf(i);
            new Thread(()->{
                book.get(b);
            },String.valueOf(i)).start();
        }
    }
}
class Book{
    private  Map<String,String> map = new HashMap<>();
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    public void put(String key,String value){
        readWriteLock.writeLock().lock();
        try{
            System.out.println(Thread.currentThread().getName()+"\t正在写入");
            map.put(key, value);
            System.out.println(Thread.currentThread().getName()+"\t写入完成");

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            readWriteLock.writeLock().unlock();
        }
    }
    public void get(String key){
        readWriteLock.readLock().lock();
        try{
            System.out.println(Thread.currentThread().getName()+"\t正在读"+key);
            String value =  map.get(key);
            System.out.println(Thread.currentThread().getName()+"\t读完成"+value);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            readWriteLock.readLock().unlock();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值