*面试重点

1.Zookeeper
1.1 选举机制
半数机制,超过半数的投票通过,即通过。
(1)第一次启动选举规则:投票过半数时,服务器id(myid)大的胜出
(2)第二次启动选举规则:①EPOCH大的直接胜出;②EPOCH相同,事务id大的胜出;③事务id相同,服务器id大的胜出

1.2 生产集群安装多少zk合适
安装奇数台。
生产经验:

  • 10台服务器:3台zk;
  • 20台服务器:5台zk;
  • 100台服务器:11台zk;
  • 200台服务器:11台zk;

服务器台数多:好处,提高可靠性;坏处:提高通信延时

1.3常用命令
ls、get、create、delete

2.
2.1
2.1.1 单例模式在多线程环境下可能存在安全问题

package com.atguigu.Interview.study.thread;

public class SingletonDemo {
    private static SingletonDemo instance = null;
    private SingletonDemo(){

        System.out.println(Thread.currentThread().getName()+"\t 我是构造方法");
    }

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

    public static void main(String[] args) {
//        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
//        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
//        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());

        System.out.println("-------------------------------------------");

        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                SingletonDemo.getInstance();
            },String.valueOf(i)).start();
        }
    }
}

解决思路:使用DCL(双端检锁)

package com.atguigu.Interview.study.thread;

public class SingletonDemo {
    private static SingletonDemo instance = null;
    private SingletonDemo(){

        System.out.println(Thread.currentThread().getName()+"\t 我是构造方法");
    }

    //DCL(Doouble Check Lock双端检锁机制)
    public static SingletonDemo getInstance(){
        if(instance == null){
            synchronized (SingletonDemo.class){
                if(instance == null){
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
//        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
//        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
//        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());

        System.out.println("-------------------------------------------");

        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                SingletonDemo.getInstance();
            },String.valueOf(i)).start();
        }
    }
}

但是:DCL机制不一定线程安全,原因是有指令重排的存在,加入volatile可以禁止指令重排
原因在于某一个线程执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化。

instance = new SingletonDemo();可以分为以下3步完成
memory = allocate(); // 1.分配对象内存空间
instance(memory); // 2.初始化对象
instance = memory; // 3.设置instance指向刚分配的内存地址,此时instance! = null

步骤2和步骤3不存在数据依赖关系,而且无论重排前还是重排后,程序的执行结果在单线程中并没有改变,因此这种重排优化是允许的。

memory = allocate(); //1.分配对象内存空间
instance = memory; //3.设置instance指向刚分配的内存地址,此时instance! = null,但是对象还没有初始化完成!
instance(memory); //2.初始化对象

指令重排只会保证串行语义的执行的一致性(单线程),但并不会关系多线程间的语义一致性。
所有当一条线程访问instance不为null时,由于instance实例未必已经初始化完成,也就造成了线程安全问题。

故对instance加上volatile修饰禁止指令重排

package com.atguigu.Interview.study.thread;

public class SingletonDemo {
    private static volatile SingletonDemo instance = null;
    private SingletonDemo(){

        System.out.println(Thread.currentThread().getName()+"\t 我是构造方法");
    }

    //DCL(Doouble Check Lock双端检锁机制)
    public static SingletonDemo getInstance(){
        if(instance == null){
            synchronized (SingletonDemo.class){
                if(instance == null){
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
//        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
//        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
//        System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());

        System.out.println("-------------------------------------------");

        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                SingletonDemo.getInstance();
            },String.valueOf(i)).start();
        }
    }
}

2.1.2 CAS

CAS的全称为Compare-And-Swap,它是一条CPU并发原语。
它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新值,这个过程是原子的。
CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中的各个方法。调用UnSafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。再次强调,由于CAS是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题。

比较并交换

package com.atguigu.Interview.study.thread;

import java.util.concurrent.atomic.AtomicInteger;

/*
* CAS是什么?===> compareAndSet
* 比较并交换
* */
public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        System.out.println(atomicInteger.compareAndSet(5, 2019)+"\t current data: " + atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5, 1024)+"\t current data: " + atomicInteger.get());
    }
}

true	 current data: 2019
false	 current data: 2019

第一个线程从主内存中拷贝的值是5,在对变量操作时会将拷贝的值和主内存的值进行比较,比较两者都是5,那么就可以对其进行操作,操作完了把新的值写入到主内存中并通知其他线程(可见性);线程二拷贝的值也是5,但是当下主内存的值是2019,操作之前进行比较时发现两边的值不一致,那么线程二的操作将取消,并将主内存中新的值拷贝到工作空间,再次比较,当工作空间的值和主内存的值一致情况下,线程二便可以对其进行操作,操作完并把其修改到主内存中通知其他线程(可见性)。真实值和期望值相同,操作成功;真实值和期望值不同,修改失败。

CAS源码解读

package com.atguigu.Interview.study.thread;

import java.util.concurrent.atomic.AtomicInteger;

/*
* CAS是什么?===> compareAndSet
* 比较并交换
* */
public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        System.out.println(atomicInteger.compareAndSet(5, 2019)+"\t current data: " + atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(5, 1024)+"\t current data: " + atomicInteger.get());
        atomicInteger.getAndIncrement();	//************************
        System.out.println(atomicInteger.get());
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

var1 : AtomicInteger对象本身
var2 : 该对象值的引用地址
var4 : 需要变动的数量
var5 : 是用var1、var2找出的主内存中真实的值。
用该对象当前的值与var5比较;如果相同,更新var5+var4并且返回true取反跳出循环,如果不同,继续取值然后再比较,直到更新完成。
在这里插入图片描述

CAS缺点
(1)循环时间长开销很大
getAndAddInt方法执行时,有个do while
如果CAS失败,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销。
(2)只能保证一个共享变量的原子操作
当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作;但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性。
(3)引出来ABA问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值