synchronized和ReentrantLock有什么区别?有人说synchronized最慢,这话靠谱吗?

典型回答

synchronized是java内建的同步机制,所以有人称其为instrinisc Locking(固有锁),它提供了互斥的语义和可见性,当一个线程已经获取当前锁时,其他试图获取的线程只能等待或者阻塞在那里。

在java5以前,synchronized是仅有的同步手段,在代码中,synchronized可以用来修饰方法,也可以使用在特定的代码块上,本质上sychronized方法等同于把方法全部语句synchronized块包起来。

ReentrantLock,通常翻译为再入锁,是java5提供的锁实现,它的语义和synchronized基本相同。再入锁通过代码直接调用lock()方法获取,代码书写更加灵活。与此同时ReentrantLock提供了很多实

用的方法,能够提供很多synchronized无法做到的细节控制,比如可以控制fairness,也就是公平性,或者利用定义条件等。但是,编码中也需要注意,必须明确调用unlock()方法释放,不然就会一直持有该锁。

synchronized和ReentrantLock的性能不能一概而论,早期版本synchronized在很多场景下性能相差较大,在后续版本进行了较多改进,在低竞争场景中表现可能优于ReentrantLock.

接下为理解锁来扩展下知识

首先,理解什么是线程安全

定义:线程安全是一个多线程环境下正确性的概念,也就是保证多线程环境下共享的,可以修改的状态的正确性,这里的状态反映在程序中其实可以看做是数据。

换个角度来看,如果状态不是共享的,或者不是可修改的,也就不存在线程安全问题,进而知道保证线程安全的两个办法:

  • 封装:通过封装,我们可以将对象内部状态隐藏,保护起来。
  • 不可变:定义变量或者对象为final,java语言目前还没有真正意义上的原生不可变。

线程安全需要保证几个基本特性:

  • 原子性,简单说就是相关操作不会中途被其他线程打扰,一般通过同步机制实现。
  • 可见性,是一个线程修改某个共享变量,其状态能够立即被其他线程知晓,通常解释为将线程本地状态反映到主内存上,volatile就是负责保证可见性的。
  • 有序性,是保证线程内串行语义,避免指令重排。

稍微有点难懂,看下代码,分析下原子性需求体现在哪里。这个例子通过取两次数值然后进行对比,来模拟两次对共享状态的操作。

你可以编译并执行,可以看到,仅仅是两个线程的低度并发,就非常容易碰到former和latter不相等的情况。这是因为,在两次取值的过程中,其他线程可能已经修改了shareState。

public class ThreadSafeSample {

    public int shareState;
    public void nonSafeAction(){
        while(shareState<100000){
                int former = shareState++;
                int latter = shareState;
                if(former !=latter - 1){
                    System.out.println("Observed data race,former is"+former+","+"latter is"+latter);
                }

        }
    }
    public static void main(String[] args) throws InterruptedException{
        final ThreadSafeSample sample = new ThreadSafeSample();
        Thread threadA = new Thread(){
            public void run(){
                sample.nonSafeAction();
            }
        };
        
        Thread threadB = new Thread(){
            public void run(){
                sample.nonSafeAction();
            }
        };
        
        threadA.start();
        threadB.start();
        threadA.join();
        threadB.join();
    }

}

运行结果

Observed data race,former is62,latter is64
Observed data race,former is8379,latter is8381
Observed data race,former is45385,latter is45387

但是如果加上 

public class ThreadSafeSample {

    public int shareState;
    public void nonSafeAction(){
        while(shareState<100000){
            synchronized(this){
                int former = shareState++;
                int latter = shareState;
                if(former !=latter - 1){
                    System.out.println("Observed data race,former is"+former+","+"latter is"+latter);
                }
            }
        }
    }
    public static void main(String[] args) throws InterruptedException{
        final ThreadSafeSample sample = new ThreadSafeSample();
        Thread threadA = new Thread(){
            public void run(){
                sample.nonSafeAction();
            }
        };
        
        Thread threadB = new Thread(){
            public void run(){
                sample.nonSafeAction();
            }
        };
        
        threadA.start();
        threadB.start();
        threadA.join();
        threadB.join();
    }

}

就不会有显示结果,这样保证了安全性

今天先写到这里

posted @ 2019-08-15 13:21  lanlei  阅读(444)  评论(0编辑  收藏  举报