面试题总结

1.请谈谈对volatile的理解

volatile是java虚拟机提供的一种轻量级的同步机制,基本遵守了JMM规范,保证多线程可见性、有序性(禁止指令重排序),不保证原子性;synchronized全都可以保证。

JMM(Java Memerory Mode):java内存模型,描述的是一种多线程下访问共享内存的规范。

对同步的规定:

线程加锁前,必须读取主内存一份最新的值到自己的工作内存;

线程解锁前,必须将自己工作空间的共享变量的最新值刷新回主内存

加锁解锁必须是同一个线程

JVM运行程序的实体是线程,JVM会为每个线程都创建一个私有的工作内存(栈空间)。Java内存模型规定所有的变量都存储主内存中,被所有的线程共享,但是线程对变量的操作必须在自己的工作空间进行,首先要从主内存拷贝一个副本到工作内存,操作完再刷新回主内存。

面试题总结_第1张图片

面试题总结_第2张图片
2.DCL(Double Check Lock)+volatile改造单例模式

传统版单例模式在多线程环境下无法保证单例

package concurent;

/**
 * @ClassName: SingletonDemo
 * @description: 单例模式
 * @author: leijing
 * @Date: 2019/7/21 下午7:31
 * @Version: 1.0
 */
public class SingletonDemo {
    private static  SingletonDemo instance;

    public SingletonDemo(){
        System.out.println("new SingletonDemo");
    }

    public static SingletonDemo getInstance(){
        if (null == instance){
            instance = new SingletonDemo();
        }
        return instance;
    }

    public static void main(String[] args) {
        //但线程下没问题
//        System.out.println(getInstance() == getInstance());
//        System.out.println(getInstance() == getInstance());
//        System.out.println(getInstance() == getInstance());
//        System.out.println(getInstance() == getInstance());

        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    getInstance();
                }
            },"t"+i).start();
        }
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
    }
    /**无法保证单例
     new SingletonDemo
     new SingletonDemo
     */
}

第二版:

package concurent;

/**
 * @ClassName: SingletonDemo
 * @description: 单例模式DCL
 * @author: leijing
 * @Date: 2019/7/21 下午7:31
 * @Version: 1.0
 */
public class SingletonDemo {
    private static  SingletonDemo instance;

    public SingletonDemo(){
        System.out.println("new SingletonDemo");
    }

    public static SingletonDemo getInstance(){
        if (null == instance){
            synchronized (SingletonDemo.class){
                if (null == instance){
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    getInstance();
                }
            },"t"+i).start();
        }
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
    }
}

运行很多次都没问题,但是可能会有指令重排序

面试题总结_第3张图片

有可能访问到还没初始化完成的对象

第三版:

package concurent;

/**
 * @ClassName: SingletonDemo
 * @description: 单例模式DCL+volatile
 * @author: leijing
 * @Date: 2019/7/21 下午7:31
 * @Version: 1.0
 */
public class SingletonDemo {
    private static volatile SingletonDemo instance;

    public SingletonDemo(){
        System.out.println("new SingletonDemo");
    }

    public static SingletonDemo getInstance(){
        if (null == instance){
            synchronized (SingletonDemo.class){
                if (null == instance){
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
    
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    getInstance();
                }
            },"t"+i).start();
        }
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
    }
}
2.什么是CAS
i++;其实是三个步骤
load i
i+1
store i
CAS(Compare And Swap)比较并交换,是操作系统的一条CPU并发原语。功能是判断内存某位置的值是否为期望值,如果是就更新为新值,这个过程是原子的,线程安全。
Unsafe是CAS的核心类,提供一系列native方法,直接调用操作系统底层资源,java不能直接访问底层系统,所以需要native方法来访问,Unsafe相当于一个后门。

CAS的核心就是Unsafe的cas和自旋锁。

private static final Unsafe unsafe = Unsafe.getUnsafe();//unsafe类保证原子性
private static final long valueOffset;

static {
    try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));//value在内存中的偏移量
    } catch (Exception ex) { throw new Error(ex); }
}

private volatile int value;
AtomicInteger.getAndIncrement() -> unsafe.getAndAddInt(this, valueOffset, 1);
/**
Object var1 对象
long var2 offset
int var4 1
**/
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;//expected
    do {
        var5 = this.getIntVolatile(var1, var2);//根据对象和偏移量拿到最新的原值
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));//如果主内存中的值与var5相等就交换,否则继续查询最新的主内存的值

    return var5;
}

缺点:

循环时间长,开销大,会导致CPU利用率飕升

只能保证一个共享变量的原子操作

有ABA问题

3.ArrayList线程安全吗 

package concurent;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * @ClassName: ContainerUnSafe
 * @description: java容器线程安全问题 
    原因:并发竞争修改导致,数据不一致,并发修改异常
    解决方案有三种:
    1.new Vector<>();
    2.Collections.synchronizedList(new ArrayList());
    3.new CopyOnWriteArrayList<>(); 写时复制,读写分离思想
 * @author: leijing
 * @Date: 2019/7/21 下午9:57
 * @Version: 1.0
 */
public class ContainerUnSafe {
    public static void main(String[] args) {
        final List list = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    list.add(UUID.randomUUID().toString().substring(0,4));
                    System.out.println(list);
                }
            }).start();
        }
    }
}
Exception in thread "Thread-2" Exception in thread "Thread-17" Exception in thread "Thread-6" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)
	at java.util.AbstractCollection.toString(AbstractCollection.java:461)
	at java.lang.String.valueOf(String.java:2994)
	at java.io.PrintStream.println(PrintStream.java:821)
	at concurent.ContainerUnSafe$1.run(ContainerUnSafe.java:22)
	at java.lang.Thread.run(Thread.java:745)

 3.哪些对象可以被垃圾回收,如何判定是垃圾,GC ROOT有哪些?

不再被引用的对象可以被垃圾回收,通过引用计数器法或者枚举根结点做可达性分析,因为引用计数器法无法解决循环引用的问题,只是一种理念不会被使用,没有被GC ROOT引用到的对象就是垃圾。GC ROOT有四种,虚拟机栈局部变量表中引用的对象、本地方法栈中引用的对象、方法区中静态成员引用的对象、方法区中常量引用的对象。

4、GC算法有哪些?GC垃圾回收器有哪些?

四种垃圾回收算法:引用计数器法、复制、标记-清除、标记-整理,算法是方法论

8种垃圾回收器:串行Serial 、 新生代并发ParNew、并行Parallel

                          老年代串行Serial Old、并发标记清除CMS、 老年代并行Parallel Old、G1、ZGC

5、可重入锁

 重入锁(递归锁):一个线程可以自动获得已经持有的锁所同步着的代码块
 synchronized/ReentrantLock是典型的可重入锁(递归锁)

package concurent;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @ClassName: ReentrantLockDemo
 * 重入锁(递归锁):一个线程可以自动获得已经持有的锁所拥有的同步方法
 * @description: synchronized/ReentrantLock是典型的可重入锁(递归锁)
 * @author: leijing
 * @Date: 2019/7/22 上午2:34
 * @Version: 1.0
 */
class Phone implements Runnable{
    //1、synchronized
    public synchronized void sendSMS(){
        System.out.println(Thread.currentThread().getName()+" sendSMS");
        sendMail();
    }
    public synchronized void sendMail(){
        System.out.println(Thread.currentThread().getName()+" sendMail");
    }
    //
    //2、ReentrantLock
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        sendLockSMS();
    }

    public  void sendLockMail(){
        lock.lock();
        try{
            System.out.println(Thread.currentThread().getName()+" sendLockMail");
        }finally {
            lock.unlock();
        }
    }

    public  void sendLockSMS(){
        lock.lock();
        try{
            System.out.println(Thread.currentThread().getName()+" sendLockSMS");
            sendLockMail();
        }finally {
            lock.unlock();
        }
    }
}
public class ReentrantLockDemo {
    public static void main(String[] args) throws InterruptedException {
        final Phone phone = new Phone();
        //1.synchronized方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                phone.sendSMS();
            }
        },"t1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                phone.sendSMS();
            }
        },"t2").start();
        Thread.sleep(1000);
        System.out.println("============================");

        /

        new Thread(phone , "t3").start();
        new Thread(phone , "t4").start();
    }

}

6.synchronized和Lock的区别

  synchronized Lock
原始构成 关键字 接口
底层实现 对象头monitorentry(1次)、monitorexit(2次确保异常也能释放)  
阻塞/唤醒方式 wait/notify/notifyAll必须在synchronized块里边才能调用 await/signal
释放方式 自动 手动
是否支持中断
是否公平 支持公平锁,默认非公平
是否支持精确唤醒 不支持,随机 支持,可以绑定多个Condition

 

你可能感兴趣的:(java)