大厂必问的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线程过来 又创建了一个对象 这样就会发生多个对象的情况

大厂必问的JUC知识_第1张图片

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语言调用内存地址

大厂必问的JUC知识_第2张图片

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

源码

大厂必问的JUC知识_第3张图片

CAS缺点

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

ABA问题

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

原子引用类型

也就是自定义一个原子类

大厂必问的JUC知识_第4张图片

原子版本号解决

类 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();
        }
    }
}

你可能感兴趣的:(Java)