java基础----Synchronized、Lock的区别与Volatile

引用了

Lock与synchronized 的区别
详解synchronized与Lock的区别与使用
Java并发编程:volatile关键字解析
volatile

['vɒlətaɪl] 易变化的

synchronized

['sɪŋkrənaɪzd] 同步的

reentrant (lock)

[ri:'entrənt] 可重入

为什么要有锁

①cpu处理以线程为单位,cpu又有一块硬件叫cache(快速缓存区),每个线程对应了自己的一块快速缓存区。因为cpu的处理速度很快,内存处理速度太慢了,所以中间有一块缓存区,我们熟悉的cache,经常听到的几级缓存。

②假设现在是单核cpu,所有的线程看起来并行,其实是串行。只是不停地在线程之间切换。

③现在两个线程执行a++,a=10。如果线程1读入a,cpu切换到了线程2,线程2读入a,结果a=11,本来应该是a=12。

④所以我们要保护值,保持值是新鲜的。于是我们使用了volatile。单线程成功了。线程1执行了a++,数据马上会被写入内存,并要求线程2重新读取a的值。我们成功了,好像不需要锁了。

⑤回到常用的多核cpu,线程1与线程2真正的同时执行了,两个核心同一时刻执行,更新变量来不及了。

⑥所以又想出了另外的一个办法,那就只能去管理过程了,这那些危险操作一个一个执行,所以到了锁。

很推荐这个----Java并发编程:volatile关键字解析

区别

√----保证 ×----不保证
3个属性更好的解释请到这里----Java并发编程:volatile关键字解析
属性 Synchronized lock volatile 解释
可见性 变量被操作之后,能够快速写入内存,并提醒其他线程重读,加锁是通过一个一个执行保证了可见性。
原子性 × 做的过程中,不要有相关的来打扰,不相关的我们也不关心,加锁是通过一个一个执行保证了流程不会被相关的打扰。
有序性 在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
很明显如果我们满足了原子性,我们就可以只使用volatile

使用场景

Volatile

通常来说,使用volatile必须具备以下2个条件:
1)对变量的写操作不依赖于当前值
2)该变量没有包含在具有其他变量的不变式中
实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。
事实上,我的理解就是上面的2个条件需要保证操作是原子性操作,才能保证使用volatile关键字的程序在并发时能够正确执行。
(转自Java并发编程:volatile关键字解析)

public class Main {

    public static volatile int a;
    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i < 10; i++){
            int finalI = i;
            new Thread(){
                @Override
                public void run() {
                    for (int j = 0; j < 1000; j++) {
                        a = finalI * j;
                    }
                }
            }.start();
        }

        for (int i = 0; i < 100; i++){
            System.out.println(a);
        }
    }
}

Synchronized(线程少的时候效率更高)----2线程 1000000次增加

public class Main{
    public volatile int inc = 0;

    public synchronized void increase() {
        inc++;
    }

    public static void main(String[] args) throws InterruptedException {
        final Main test = new Main();
        long a = System.currentTimeMillis();
        for(int i=0;i<2;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<1000000;j++) {
                        test.increase();
                    }
                    System.out.println(System.currentTimeMillis() - a + "ms");
                }
            }.start();
        }

        Thread.sleep(1000);
        System.out.println(test.inc);
    }
}

输出:65ms 2000000
public class Main{
    public volatile int inc = 0;

    public ReentrantLock lock = new ReentrantLock();
    public void increase() {
        lock.lock();
        inc++;
        lock.unlock();
    }

    public static void main(String[] args) throws InterruptedException {
        final Main test = new Main();
        long a = System.currentTimeMillis();
        for(int i=0;i<2;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<1000000;j++) {
                        test.increase();
                    }
                    System.out.println(System.currentTimeMillis() - a + "ms");
                }
            }.start();
        }

        Thread.sleep(1000);
        System.out.println(test.inc);
    }
}

输出:100ms 2000000

ReentrantLock(线程多的时候效率更高)----200线程 10000次增加

public class Main{
    public volatile int inc = 0;

    public ReentrantLock lock = new ReentrantLock();
    public void increase() {
        lock.lock();
        inc++;
        lock.unlock();
    }

    public static void main(String[] args) throws InterruptedException {
        final Main test = new Main();
        long a = System.currentTimeMillis();
        for(int i=0;i<200;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<10000;j++) {
                        test.increase();
                    }
                    System.out.println(System.currentTimeMillis() - a + "ms");
                }
            }.start();
        }

        Thread.sleep(1000);
        System.out.println(test.inc);
    }
}

输出:91ms 2000000
public class Main{
    public volatile int inc = 0;

    public synchronized void increase() {
        inc++;
    }

    public static void main(String[] args) throws InterruptedException {
        final Main test = new Main();
        long a = System.currentTimeMillis();
        for(int i=0;i<200;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<10000;j++) {
                        test.increase();
                    }
                    System.out.println(System.currentTimeMillis() - a + "ms");
                }
            }.start();
        }

        Thread.sleep(1000);
        System.out.println(test.inc);
    }
}

输出:196ms 2000000

比较

类别 synchronized Lock
存在层次 Java的关键字,在jvm层面上 是一个类
锁的释放 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 在finally中必须释放锁,不然容易造成线程死锁
锁的获取 假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待 分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待
锁状态 无法判断 可以判断
锁类型 可重入 不可中断 非公平 可重入 可判断 可公平(两者皆可)
性能 少量同步 大量同步
回到为什么线程少的时候Synchronized快,多的时候慢
引用了:https://blog.csdn.net/ganyao939543405/article/details/52486316

A).一般认为synchronized关键字的实现是源自于像信号量之类的线程同步机制,涉及到线程运行状态的切换,在高并发状态下,CPU消耗过多的时间在线程的调度上,从而造成了性能的极大浪费。
B).lock实现原理则是依赖于硬件,现代处理器都支持CAS指令,所谓CAS指令简单的来说Compare And Set,CPU循环执行指令直到得到所期望的结果,换句话来说就是当变量真实值不等于当前线程调用时的值的时候(说明其他线程已经将这个值改变),就不会赋予变量新的值。这样就保证了变量在多线程环境下的安全性。

当JDK版本高于1.6的时候,synchronized已经被做了CAS的优化:具体是这样的,当执行到synchronized代码块时,先对对象头的锁标志位用lock cmpxchg的方式设置成“锁住“状态,释放锁时,在用lock cmpxchg的方式修改对象头的锁标志位为”释放“状态,写操作都立刻写回主内存。JVM会进一步对synchronized时CAS失败的那些线程进行阻塞操作(调用操作系统的信号量)(此段来摘自别处)。也就是先CAS操作,不行的话继而阻塞线程。

除此之外,系统环境,CPU架构,虚拟机环境都会影响两者的性能关系。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容