`
jieke_ZJ
  • 浏览: 43025 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

关于synchronized与lock的性能比较

 
阅读更多

记得当初看教程的时候大家都说lock性能比好不少,最近需要自己设计一个缓存终于要自己尝试一番了。

1.关于两者的实现的比较

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架构,虚拟机环境都会影响两者的性能关系。

2.用数据说话

1).X86_64 cpu i7 4910mq @4.0ghz ,Windows10 64bit,JDK1.8 hotspot 64bit虚拟机环境

测试代码

测试对某Map对象高并发下的读写线程安全测试
测试对比有synchronized,ReadWriteLock,ConcurrentHashMap,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class MapTest {
 
     private Map<integer,string> map = new ConcurrentHashMap<>();
 
     private long starttime;
 
     private AtomicInteger count = new AtomicInteger(t_count);
 
     private final static int t_count = 5000;
 
     private final static int rw_count = 10000;
 
        Runnable readrun = new Runnable() {
            @Override
            public void run() {
                int i = rw_count;
                while (i > 0){
                    map.get(i);
                    i--;
                }
                System.out.println("read-mapsize="+map.size());
                if(count.decrementAndGet() == 0)
                    System.out.println("time="+ (System.currentTimeMillis() - starttime +"ms"));
            }
        };
 
        Runnable writerun = new Runnable() {
            @Override
            public void run() {
                int i = rw_count;
                while (i > 0){
                    map.put(i,i+"");
                    i--;
                }
                System.out.println("write-mapsize="+map.size());
                if(count.decrementAndGet() == 0)
                    System.out.println("time="+ (System.currentTimeMillis() - starttime + "ms"));
            }
        };
 
        public void run(){
            starttime = System.currentTimeMillis();
            for(int i = 0;i < t_count/2;i ++){
                new Thread(writerun).start();
                new Thread(readrun).start();
            }
        }
}
</integer,string>

HashMap 用synchronized重写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class SyncHashMap extends HashMap{
 
    @Override
    public Object get(Object key) {
        // TODO Auto-generated method stub
        synchronized (this) {
            return super.get(key);
        }
    }
 
    @Override
    public synchronized Object put(Object key, Object value) {
        // TODO Auto-generated method stub
        synchronized (this) {
            return super.put(key, value);
        }
 
    }
 
}

用读写锁实现的Map代理类,有些粗糙,没加try finally

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public class SyncMapProxy<k,v> implements Map<k,v>{
 
    private Map<k,v> origin;
    private ReadWriteLock lock;
 
    public SyncMapProxy(Map<k, v=""> origin) {
        this.origin = origin;
        lock = new ReentrantReadWriteLock();
    }
 
    public static  <k,v> SyncMapProxy<k,v> SyncMap(Map<k,v> map){
        return new SyncMapProxy<k,v>(map);
    }
 
    @Override
    public void clear() {
        lock.writeLock().lock();
        origin.clear();
        lock.writeLock().unlock();
    }
 
    @Override
    public boolean containsKey(Object key) {
        lock.readLock().lock();
        boolean res = origin.containsKey(key);
        lock.readLock().unlock();
        return res;
    }
 
    @Override
    public boolean containsValue(Object value) {
        lock.readLock().lock();
        boolean res = origin.containsKey(value);
        lock.readLock().unlock();
        return res;
    }
 
    @Override
    public Set<entry<k, v="">> entrySet() {
        lock.readLock().lock();
        Set<entry<k, v="">> res = origin.entrySet();
        lock.readLock().unlock();
        return res;
    }
 
    @Override
    public V get(Object key) {
        lock.readLock().lock();
        V res = origin.get(key);
        lock.readLock().unlock();
        return res;
    }
 
    @Override
    public boolean isEmpty() {
        return origin.isEmpty();
    }
 
    @Override
    public Set<k> keySet() {
        lock.readLock().lock();
        Set<k> res = origin.keySet();
        lock.readLock().unlock();
        return res;
    }
 
    @Override
    public V put(K key, V value) {
        lock.writeLock().lock();
        V v = origin.put(key, value);
        lock.writeLock().unlock();
        return v;
    }
 
    @Override
    public void putAll(Map<!--? extends K, ? extends V--> map) {
        lock.writeLock().lock();
        origin.putAll(map);
        lock.writeLock().unlock();
    }
 
    @Override
    public V remove(Object key) {
        lock.writeLock().lock();
        V v = origin.remove(key);
        lock.writeLock().unlock();
        return v;
    }
 
    @Override
    public int size() {
        return origin.size();
    }
 
    @Override
    public Collection<v> values() {
        lock.readLock().lock();
        Collection<v> res = origin.values();
        lock.readLock().unlock();
        return res;
    }
}
</v></v></k></k></entry<k,></entry<k,></k,v></k,v></k,v></k,v></k,></k,v></k,v></k,v>

并发量100000,每个线程对Map执行读写100次,总耗时
ConcurrentHashMap:6112ms
synchronized:6121ms
ReadWriteLock:6182ms
Collections.synchronizedMap:6175ms

并发量10000,每个线程对Map执行读写1000次,总耗时
ConcurrentHashMap:1126ms
synchronized:1145ms
ReadWriteLock:2086ms
Collections.synchronizedMap:1170ms

并发量5000,每个线程对Map执行读写10000次,总耗时
ConcurrentHashMap:1206ms
synchronized:4896ms
ReadWriteLock:8505ms
Collections.synchronizedMap:4883ms

并发量1000,每个线程对Map执行读写100000次,总耗时
ConcurrentHashMap:1748ms
synchronized:9341ms
ReadWriteLock:18720ms
Collections.synchronizedMap:8945ms

并发量100,每个线程对Map执行读写1000000次,总耗时
ConcurrentHashMap:1922ms
synchronized:8417ms
ReadWriteLock:16110ms
Collections.synchronizedMap:9604ms

事实证明在以上的配置环境JDK1.8 X86 Windows10下,高并发下这几种方式性能都相差无几,较高和较低并发下,synchronized都比ReadWriteLock来的快,基本是两倍的关系。ConcurrentHashMap作为同步的Map还是时间性能还是最高的。总之在hotspot下都是一个数量级的。


2).下面看另外一种环境
Android6.0 X86_64模拟器镜像,ART Runtime

并发量20,每个线程对Map执行读写1000000次,总耗时
ConcurrentHashMap:10841ms
synchronized:239452ms
ReadWriteLock:16450ms
Collections.synchronized:213429ms

并发量200,每个线程对Map执行读写10000次,总耗时
ConcurrentHashMap:973ms
synchronized:57047ms
ReadWriteLock:1274ms
Collections.synchronized:52746ms

**难以置信的性能差距,synchronized和Lock在Android的Art环境下确实有着一个数量级的差距,可达数十倍之多,但是在Hotspot环境下却恰恰相反,lock在多数情况下反而不如synchronized。
这里估计是Android Art虚拟机尚未对synchronized进行CAS优化,主要还是因为Android现在作为客户端操作系统,对高并发的资源竞争并无必要做优化,以上结果尚不能下定论,看来要去扒一扒Art的源码才能知道具体的原因了。**

总结

如果开发的是服务器程序,并且使用的是最新的hotspot虚拟机,synchronized和lock其实已经相差无几,其底层实现已经差不多了。但是如果你是Android开发者,使用synchronized还是需要考虑其性能差距的。

分享到:
评论

相关推荐

    Lock接口与synchronized关键字

    Lock接口与synchronized关键字在Java并发编程中都是用于实现同步机制的重要工具,但它们在使用方式、功能特性以及灵活性等方面存在一些显著的差异。 首先,从使用方式上来看,synchronized是Java语言内置的关键字,...

    lock锁,lock锁和synchronized的对比

    # synchronized锁与lock锁的对比 Lock是显式锁,需要手动的开启和关闭,synchronized锁是隐式锁,只要出了作用域就会自动释放。Lock只有代码块锁,synchronized既有代码块锁还有方法锁。 使用Lock锁,JVM将花费较...

    lock4j高性能分布式锁.rar

    分布式锁概念当我们在单机环境下进行应用开发,涉及到并发同步时,说到如何保证线程的安全,相信大家一定能想到采用synchronized或者Lock的方式,来解决多线程间的代码同步问题。这时多线程的运行都是在同一个JVM之...

    java关键字Synchronized详解

    在Java中,锁膨胀(Lock Inversion)是一个重要的概念。当一个对象被多个线程同时持有锁时,可能会导致锁膨胀现象。锁膨胀是指原本互斥的两个锁,由于某些原因,变得不再互斥。这会导致原本同步的代码段出现竞态条件,...

    Java学习题答案

    (15分) 主要相同点: Lock能完成synchronized所实现的所有功能.(其它不重要) 主要不同点: Lock有比synchronized更精确的线程语义和更好的性能(在相同点中回答此点也行) synchronized会自动释放锁....

    Java中ReentrantLock的使用.docx

    重入锁ReentrantLock 相对来说是synchronized、Object.wait()和Object.notify()方法的替代品(或者说是增强版),在JDK5.0的早期版本,重入锁的性能远远好于synchronized,但从JDK6.0开始,JDK在synchronized上做了...

    java多线程安全性基础介绍.pptx

    java多线程安全性基础介绍 线程安全 正确性 什么是线程安全性 原子性 ... 性能 线程活跃与线程饥饿 同步工具类 原子操作类 AtomicInteger等 相当于加上synchronized 也有些利用硬件底层能力CAS

    阿里百度美团面试题合集

    Synchronized 和 Lock 哪个更好? . HashMap 中的 get()方法是如何实现的? . HashMap 可以用在哪些场景? . JVM,垃圾回收机制,内存划分等 . SQL 优化,常用的索引? . 还有什么问题需要问的。 多线程状态图,...

    面试官:有没有比读写锁更快的锁?

    目录面试三连什么是读写锁StampedLock横空出世StampedLock三种模式基本语法StampedLock完整的demo让StampedLock性能更上一楼的乐观读你了解乐观读的应用场景吗使用StampedLock的注意事项总结 面试三连 面试官:了解...

    Java开发常见问题总结.docx

    一些关键且实用的Java开发技巧: 基础语法与规范: 始终使用public class并遵循...使用synchronized关键字或Lock接口进行同步控制。 利用ExecutorService进行多线程任务管理。 注意volatile关键字的使用,确保多线程

    JUC多线程学习个人笔记

    原子操作:JUC提供了一些原子操作类,如AtomicInteger、AtomicLong等,可以实现线程安全的原子操作,避免了使用synchronized关键字的性能损耗。 锁机制:JUC提供了Lock接口和Condition接口,可以实现更细粒度的锁...

    Java-并发(Concurrent)编程

    资源概要:1,多线程;2,synchronized;3,volatile;4,多线程在JVM中的实现原理剖析 导语: ...锁Lock-AQS核心原理剖析 并发工具类、并发容器、阻塞队列 线程池原理剖析 线程池案例-Web容器-压力测试

    第九讲:分布式锁的原理及应用&秒杀设计实现.pdf

    synchronized、Lock 分布式锁(多服务共享锁) 在分布式的部署环境下,通过锁机制来让多客户 端互斥的对共享资源进行访问 8 分布式锁的基本概念  基本概念 * 多任务环境中才需要 * 任务都需要对同一共享资源进行...

    tinylog:A lightweight C,C++ log component developed for Linux, It is designed with high performance, asynchronized, thread-safe and process-safe; tinylog是一个专为UNIX设计的轻量级的CC++日志模块,其提供了高性能,异步,线程安全,进程安全的日志功能

    Tinylog是用于UNIX环境的轻量级C语言高性能日志组件,它是用于C / C ++的高性能,异步,线程安全和进程安全的日志库。 它支持日志归档,异步,多线程写入,多处理写入,非阻塞模式。 输出示例: [ 2018 - 04 - 03...

    JAVA高质量并发详解,多线程并发深入讲解

    接着,深入讲解了Java并发编程的核心API,如synchronized关键字、Lock接口、Condition接口、Semaphore等,帮助读者掌握Java并发编程的基本工具和方法。 除了基础知识和API的讲解,本书还重点介绍了Java并发编程的...

    什么是线程?Java中如何创建和管理线程?(java面试题附答案).txt

    通过将 MyRunnable 对象传递给 Thread 类的构造方法,我们创建了一个新的线程,并将 run 方法...通过合理地创建和管理线程,我们可以实现复杂的并发执行逻辑,提高程序的性能和响应能力,并确保线程之间的安全和协调。

    Java并发编程实战

    第三部分 活跃性、性能与测试 第10章 避免活跃性危险169 10.1 死锁169 10.1.1 锁顺序死锁170 10.1.2 动态的锁顺序死锁171 10.1.3 在协作对象之间发生的死锁174 10.1.4 开放调用175 10.1.5 资源死锁177 10.2...

    Java高级工程师简历模板18k+

    2.熟练使用常用的java集合类以及常用集合的源码,熟悉多线程以及同步容器以及并发容器的使用,AQS,CAS,lock,volatilte,synchronized等; 3.对Java虚拟机、JMM、垃圾收集机制、GC算法、JVM常用配置参数、GC参数、...

    浅谈分布式锁的几种使用方式(redis、zookeeper、数据库)

    synchronized lock dblock Q:两个业务服务器,一个数据库,操作:查询用户当前余额,扣除当前余额的3%作为手续费 分布式锁 我们需要怎么样的分布式锁? 可以保证在分布式部署的应用集群中,同一个方法在同一...

    java多线程设计模式详解(PDF及源码)

    单线程程序 多线程程序 Thread类的run方法和start方法 线程的启动 线程的启动(1)——利用Thread类的子类 线程的启动(2)——利用Runnable接口 线程的暂时停止 线程的共享互斥 synchronized方法 synchronized阻挡 ...

Global site tag (gtag.js) - Google Analytics