亲宝软件园·资讯

展开

Java多线程同步优化 Java多线程的同步优化的6种方案

卜大爷 人气:0
想了解Java多线程的同步优化的6种方案的相关内容吗,卜大爷在本文为您仔细讲解Java多线程同步优化的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:Java多线程同步优化,Java多线程同步,下面大家一起来学习吧。

概述

处理器上的寄存器的读写的速度比内存快几个数量级,为了解决这种速度矛盾,在它们之间加入了高速缓存。

加入高速缓存带来了一个新的问题:缓存一致性。如果多个缓存共享同一块主内存区域,那么多个缓存的数据可能会不一致,需要一些协议来解决这个问题。

在Java内存模型中,分为主内存和线程工作内存,线程使用共享数据时,先从主内存中拷贝数据到工作内存,使用完成之后再写入主内存中。

在Java中,有多线程并发时,我们可以使用多线程同步的方式来解决内存一致性的问题。通常我们可以在程序中添加同步锁来保障数据的安全访问,但是也经常会带来一些同步性能问题,那么本章将针对常见的同步问题给出了一些优化方案。

读写锁

在多线程操作下,如果我们的某些数据经常被读取操作,但非常少的时机被写入操作。这时,如果我们使用synchronized等同步方式,性能会非常低。

这种场景下,我们应该使用读写锁来进行优化。

读写锁的特点:

某些特定的场景,使用读写锁会极大的提高多线程并发操作的效率。因为,读写锁中,读锁不是排它锁,所以可以并发执行,可以非常显著的提高读取效率;只有在写锁时,是排它锁,这时需要等待写锁的释放。

ReetrantReadWriteLock

ReadWriteLock接口

Java并发包中ReadWriteLock是一个接口,抽象了读写锁方法:

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading
     */
    Lock readLock();

    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing
     */
    Lock writeLock();
}

ReadWriteLock管理一组锁,一个是只读的锁,一个是写锁。

ReetrantReadWriteLock类

Java并发库中ReetrantReadWriteLock实现了ReadWriteLock接口并添加了可重入的特性。

1. ReetrantReadWriteLock获取锁顺序有两种模式:

2. 可重入

ReetrantReadWriteLock锁是可重入的,当然一个线程获取多少次锁,就必须释放多少次锁。

3. 锁降级

在读写锁中,锁降级:从写锁变成读锁;锁升级:从读锁变成写锁。

读写锁状态的设计

读写锁的状态是用一个int值来表示的。state(int32位)字段分成高16位与低16位,其中高16位表示读锁个数,低16位表示写锁个数。

例如,当前一个线程获取到了写锁,并且重入了两次,因此低16位是3,并且该线程又获取了读锁,并且重入了一次,所以高16位是2,当写锁被获取时如果读锁不为0那么读锁一定是获取写锁的这个线程。

写时复制

写时复制(Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者同时要求相同资源,他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的。此作法主要的优点是如果调用者没有修改该资源,就不会有副本被创建,因此多个调用者只是读取操作时可以共享同一份资源。

在Java中,Copy on Write这种机制通常用在集合上,在并发访问的情景下,当需要修改JAVA中Containers的元素时,不直接修改该容器,而是先复制一份副本,在副本上进行修改。修改完成之后,将指向原来容器的引用指向新的容器(副本容器)。

写时复制的特点

Java写时复制容器类

JDK中提供了CopyOnWriteArrayList类和CopyOnWriteArraySet类,实现了写时复制。

减小锁的粒度

如果我们在一个大的数据操作类里面,大量使用了锁,并且还是同一个锁,这时,我们的多线程同步效率就会变得非常低。

我们可以将数据按照不同的类型及应用场景进行分割,然后用不同的锁进行同步,这样,不同的场景下就不会产生排它锁的冲突问题,可以大大提高同步的效率。

该方案简单来说就是将一个大锁,分割成多个小锁,这样就能显著的提高多线程并发执行的效率。

减小锁的占有时间

如果在一个较大的方法中,我们直接给该方法加了一个锁,但是我们需要同步的地方只是该方法中的一行操作代码,这样就是很糟糕的同步使用方式了。

我们可以将锁细化到使用它的代码行上,而不是整个函数都加锁,这样锁的持有时间就会变少,从而提高了多线程同步的性能。

该方案是将同步块的代码范围减小,从而降低锁的持有时间,达到优化多线程同步性能的目的。

锁粗化

虽然说,减少锁的占有时间可以提高性能,但是有时候,这种方式并不适用。

例如,一个循环中,我们在循环体中,使用了锁,这样反而会降低性能,这时我们应该在循环开始之前加锁,结束之后释放,也就是将锁粗化。

这是为什么呢?

这是因为,频繁的对锁进行请求、释放、状态修改等操作,会造成大量系统资源的消耗,从而降低性能。

ThreadLocal

同步效率低,是因为多线程同步等待造成的,那么我们可以换一个思路,如果让每个线程都持有一份数据,那这样就不会存在竞争的问题了,也就不需要同步锁了。这样就会很大程度上提高多线程并发的性能。

关于ThreadLocal相关实现原理及使用可以参考之前的文章《ThreadLocal线程本地对象原理分析》。

总结

Java中可以使用锁来解决多线程的同步问题,保障了数据的一致性,但也会代理很多问题,本章总结了多线程同步的几种优化方案:

加载全部内容

相关教程
猜你喜欢
用户评论