多线程与高并发编程二 synchronized,synchronized 和 Lock对比

首先看下 饿汉式 单例模式

/**
 * 饿汉式 单例模式
 */
public class MyThread {

    //初始化就 new 一个
    static final MyThread myThread = new MyThread();

    //定义为私有,不允许再new
    private MyThread(){}

    //直接把生成好的给他
    public static MyThread getInstance(){
        return myThread;
    }

    public static void main(String[] args) {
        MyThread m1 = MyThread.getInstance();
        MyThread m2 = MyThread.getInstance();
        System.out.println(m1 == m2);
    }
}

打印结果为true。 

使用volatile 和synchronized 修改

public class MyThread {

    static volatile MyThread myThread;

    //定义为私有,不允许再new
    private MyThread(){}

    /**
     * 直接锁定该方法块
     * 即使多线程并发,同时也只能有一个线程进入
     * @return
     */
    public static synchronized MyThread getInstance(){
        if(myThread == null){
            myThread = new MyThread();
        }
        return myThread;
    }

    /**
     * double check  防止多线程并发同时进入
     * @return
     */

    public static MyThread getInstance2(){
        if(myThread == null){
            synchronized (MyThread.class){
                if(myThread == null){
                    myThread = new MyThread();
                }
            }
        }
        return myThread;
    }

new  对象的过程: 3 步。  至于为什么是3步,有兴趣的可以百度下汇编的 new 对象的代码。这里理解为汇编语言需要3步。

多线程与高并发编程二 synchronized,synchronized 和 Lock对比_第1张图片

 为什么使用了synchronized  还需要volatile关键字呢。

synchronized :原子性

volatile : 可见性

不管是new 对象还是 i++ 或者其它,都不是原子性的。加了synchronized 的方法或块,只能说方法或块中的代码是原子性的。

被synchronized 修饰的方法或块,同一时间不能被其它线程执行。但是其它线程可以执行其它方法,或者调用对象成员变量。

    public boolean i = true;

    public void setI100(){
        while (i){

        }
        System.out.println("  end");
    }


    public static void main(String[] args) {
        MyThread my = new MyThread();

        new Thread(my::setI100,"my").start();
        try {
            Thread.sleep(1000);
            my.i = false;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(my.i);
    }

结果:不会打印end

如上代码,线程 运行 setI100  方法,由于取到的是true,所以一直在循环,而这时,  main 线程中已经修改为false,但是setI100 方法不知道,还是之前的。所以会一直循环。

public volatile boolean i = true;

这时加上如上代码,就会打印出end。

volatile : 可见性,防止代码重排序。

可见性实现:缓存一致性协议 MSI             禁止指令重排实现:JMM,内存屏障

 

-------------------------------------------------

如果synchronized  修饰对象,被synchronized 锁定的对象必须是final 的。

public class MyThread {

    public Object object = new Object();

    void m(){
        synchronized (object){
            try {
                TimeUnit.MICROSECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        }
    }


    public static void main(String[] args) {
        MyThread my = new MyThread();

        new Thread(my::m,"my").start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Thread tw = new Thread(my::m,"tw");
        my.object = new Object();
        tw.start();
    }
}

如上代码,如果在main 中修改了 object 代码。

根据synchronized 锁的机制,锁的是 Object 对象这个 mark word 中的两位标识,但是main已经改变了object,所以锁的已经不同同一个对象了。

这时候应该在object 前加一个 final ,禁止被修改。

 

CAS 自旋锁(无锁优化,自旋)

compare and set:比较和设值。保存旧值,更改后保存前,对比下,如果值不一致,则进行再循环。

CAS 是cpu 原语支持的,指令级操作,不能被打断。

 

synchronized 和 Lock对比

1.synchronized 内置的java关键字。Lock是一个java接口,有三个实现类,ReentrantLock、ReentrantReadWriteLock(读写锁)、ReentrantReadWriteLock.WriteLock(写锁)

2.synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁

3.synchronized 会自动释放锁,lock必须手动释放

4.Synchronized 适合锁少量的代码同步问题,lock适合锁大量的同步代码。

你可能感兴趣的:(多线程JUC编程)