精华内容
下载资源
问答
  • 线程安全的实现方法1.互斥同步最基本的互斥同步手段就是synchronized关键字,synchronized关键字经过编译之后,会在同步块的... 如果Java程序中的synchronized明确指定了对象参数,那就是这个对象的reference;如...

    线程安全的实现方法

    1.互斥同步

    最基本的互斥同步手段就是synchronized关键字,synchronized关键字经过编译之后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令,这两个字节码都需要一个reference类型的参数来指明要锁定和解锁的对象。 如果Java程序中的synchronized明确指定了对象参数,那就是这个对象的reference;如果没有明确指定,那就根据synchronized修饰的是实例方法还是类方法,去取对应的对象实例或Class对象来作为锁对象。
    在执行monitorenter指令时,首先要尝试获取对象的锁。 如果这个对象没被锁定,或者当前线程已经拥有了那个对象的锁,把锁的计数器加1,相应的,在执行monitorexit指令时会将锁计数器减1,当计数器为0时,锁就被释放。 如果获取对象锁失败,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止。
    java.util.concurrent(下文称J.U.C)包中的重入锁(ReentrantLock)来实现同步
    等待可中断是指当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情,可中断特性对处理执行时间非常长的同步块很有帮助。
    锁绑定多个条件是指一个ReentrantLock对象可以同时绑定多个Condition对象,而在synchronized中,锁对象的wait()和notify()或notifyAll()方法可以实现一个隐含的条件,如果要和多于一个的条件关联的时候,就不得不额外地添加一个锁,而ReentrantLock则无须这样做,只需要多次调用newCondition()方法即可。

    公平锁与非公平锁

    公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁;而非公平锁则不保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获得锁。synchronized中的锁是非公平的,ReentrantLock默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁。

    悲观锁

    互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施(例如加锁),那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、 用户态核心态转换、 维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。

    乐观锁

    基于冲突检测的乐观并发策略,通俗地说,就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就再采取其他的补偿措施(最常见的补偿措施就是不断地重试,直到成功为止),这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步。
    乐观并发策略需要“硬件指令集的发展”才能进行呢?因为我们需要操作和冲突检测这两个步骤具备原子性,靠什么来保证呢?如果这里再使用互斥同步来保证就失去意义了,所以我们只能靠硬件来完成这件事情,硬件保证一个从语义上看起来需要多次操作的行为只通过一条处理器指令就能完成。
    • 测试并设置(Test-and-Set)。
    • 获取并增加(Fetch-and-Increment)。
    • 交换(Swap)。
    • 比较并交换(Compare-and-Swap,下文称CAS)。
    • 加载链接/条件存储(Load-Linked/Store-Conditional,下文称LL/SC)。

    自旋锁

    -XX:+UseSpinning参数来开启。
    如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间,比如100个循环。 另外,如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源。

    轻量级锁和偏向锁

    HotSpot虚拟机的对象头(Object Header)分为两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、 GC分代年龄(Generational GC Age)等,这部分数据的长度在32位和64位的虚拟机中分别为32bit和64bit,官方称它为“Mark Word”,它是实现轻量级锁和偏向锁的关键。

    展开全文
  • java中有哪些这个问题在我看了一遍后尽然无法回答,说明自己对于的概念了解的不够。于是再次翻看了一下里的内容,突然有点打开脑门的感觉。看来确实是要学习的最好方式是要带着问题去学,并且解决问题。在java...

    java中有哪些锁

    这个问题在我看了一遍后尽然无法回答,说明自己对于锁的概念了解的不够。于是再次翻看了一下书里的内容,突然有点打开脑门的感觉。看来确实是要学习的最好方式是要带着问题去学,并且解决问题。

    在java中锁主要两类:内部锁synchronized和显示锁java.util.concurrent.locks.Lock。但细细想这貌似总结的也不太对。应该是由java内置的锁和concurrent实现的一系列锁。

    为什么这说,因为在java中一切都是对象,而java对每个对象都内置了一个锁,也可以称为对象锁/内部锁。通过synchronized来完成相关的锁操作。

    而因为synchronized的实现有些缺陷以及并发场景的复杂性,有人开发了一种显式的锁,而这些锁都是由java.util.concurrent.locks.Lock派生出来的。当然目前已经内置到了JDK1.5及之后的版本中。

    synchronized

    首先来看看用的比较多的synchronized,我的日常工作中大多用的也是它。synchronized是用于为某个代码块的提供锁机制,在java的对象中会隐式的拥有一个锁,这个锁被称为内置锁(intrinsic)或监视器锁(monitor locks)。线程在进入被synchronized保护的块之前自动获得这个锁,直到完成代码后(也可能是异常)自动释放锁。内置锁是互斥的,一个锁同时只能被一个线程持有,这也就会导致多线程下,锁被持有后后面的线程会阻塞。正因此实现了对代码的线程安全保证了原子性。

    可重入

    既然java内置锁是互斥的而且后面的线程会导致阻塞,那么如果持有锁的线程再次进入试图获得这个锁时会如何呢?比如下面的一种情况:

    public class BaseClass {

    public synchronized void do() {

    System.out.println("is base");

    }

    }

    public class SonClass extends BaseClass {

    public synchronized void do() {

    System.out.println("is son");

    super.do();

    }

    }

    SonClass son = new SonClass();

    son.do();

    此时派生类的do方法除了会首先会持有一次锁,然后在调用super.do()的时候又会再一次进入锁并去持有,如果锁是互斥的话此时就应该死锁了。

    但结果却不是这样的,这是因为内部锁是具有可重入的特性,也就是锁实现了一个重入机制,引用计数管理。当线程1持有了对象的锁a,此时会对锁a的引用计算加1。然后当线程1再次获得锁a时,线程1还是持有锁a的那么计算会加1。当然每次退出同步块时会减1,直到为0时释放锁。

    synchronized的一些特点

    修饰代码的方式

    修饰方法

    public class BaseClass {

    public synchronized void do() {

    System.out.println("is base");

    }

    }

    这种就是直接对某个方法进行加锁,进入这个方法块时需要获得锁。

    修饰代码块

    public class BaseClass {

    private static Object lock = new Object();

    public void do() {

    synchronized (lock) {

    System.out.println("is base");

    }

    }

    }

    这里就将锁的范围减少到了方法中的部分代码块,这对于锁的灵活性就提高了,毕竟锁的粒度控制也是锁的一个关键问题。

    对象锁的类型

    经常看到一些代码中对synchronized使用比较特别,看一下如下的代码:

    public class BaseClass {

    private static Object lock = new Object();

    public void do() {

    synchronized (lock) {

    }

    }

    public synchronized void doVoid() {

    }

    public synchronized static void doStaticVoid() {

    }

    public static void doStaticVoid() {

    synchronized (BaseClass.class) {

    }

    }

    }

    这里出现了四种情况:修饰代码块,修饰了方法,修饰了静态方法,修饰BaseClass的class对象。那这几种情况会有什么不同呢?

    修饰代码块

    这种情况下我们创建了一个对象lock,在代码中使用synchronized(lock)这种形式,它的意思是使用lock这个对象的内置锁。这种情况下就将锁的控制交给了一个对象。当然这种情况还有一种方式:

    public void do() {

    synchronized (this) {

    System.out.println("is base");

    }

    }

    使用this的意思就是当前对象的锁。这里也道出了内置锁的关键,我提供一把锁来保护这块代码,无论哪个线程来都面对同一把锁咯。

    修饰对象方法

    这种直接修饰在方法是咱个情况?其实和修饰代码块类似,只不过此时默认使用的是this,也就是当前对象的锁。这样写起代码来倒也比较简单明确。前面说过了与修饰代码块的区别主要还是控制粒度的区别。

    修饰静态方法

    静态方法难道有啥不一样吗?确实是不一样的,此时获取的锁已经不是this了,而this对象指向的class,也就是类锁。因为Java中的类信息会加载到方法常量区,全局是唯一的。这其实就提供了一种全局的锁。

    修饰类的Class对象

    这种情况其实和修改静态方法时比较类似,只不过还是一个道理这种方式可以提供更灵活的控制粒度。

    小结

    通过这几种情况的分析与理解,其实可以看内置锁的主要核心理念就是为一块代码提供一个可以用于互斥的锁,起到类似于开关的功能。

    java中对内置锁也提供了一些实现,主要的特点就是java都是对象,而每个对象都有锁,所以可以根据情况选择用什么样的锁。

    java.util.concurrent.locks.Lock

    前面看了synchronized,大部分的情况下差不多就够啦,但是现在系统在并发编程中复杂性是越来越高,所以总是有许多场景synchronized处理起来会比较费劲。或者像中说的那样,concurrent中的lock是对内部锁的一种补充,提供了更多的一些高级特性。

    java.util.concurrent.locks.Lock简单分析

    这个接口抽象了锁的主要操作,也因此让从Lock派生的锁具备了这些基本的特性:无条件的、可轮循的、定时的、可中断的。而且加锁与解锁的操作都是显式进行。下面是它的代码:

    public interface Lock {

    void lock();

    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    void unlock();

    Condition newCondition();

    }

    ReentrantLock

    ReentrantLock就是可重入锁,连名字都这么显式。ReentrantLock提供了和synchronized类似的语义,但是ReentrantLock必须显式的调用,比如:

    public class BaseClass {

    private Lock lock = new ReentrantLock();

    public void do() {

    lock.lock();

    try {

    //....

    } finally {

    lock.unlock();

    }

    }

    }

    这种方式对于代码阅读来说还是比较清楚的,只不过有个问题,就是如果忘了加try finally或忘 了写lock.unlock()的话导致锁没释放,很有可能导致一些死锁的情况,synchronized就没有这个风险。

    trylock

    ReentrantLock是实现Lock接口,所以自然就拥有它的那些特性,其中就有trylock。trylock就是尝试获取锁,如果锁已经被其他线程占用那么立即返回false,如果没有那么应该占用它并返回true,表示拿到锁啦。

    另一个trylock方法里带了参数,这个方法的作用是指定一个时间,表示在这个时间内一直尝试去获得锁,如果到时间还没有拿到就放弃。

    因为trylock对锁并不是一直阻塞等待的,所以可以更多的规避死锁的发生。

    lockInterruptibly

    lockInterruptibly是在线程获取锁时优先响应中断,如果检测到中断抛出中断异常由上层代码去处理。这种情况下就为一种轮循的锁提供了退出机制。为了更好理解可中断的锁操作,写了一个demo来理解。

    package com.test;

    import java.util.Date;

    import java.util.concurrent.locks.ReentrantLock;

    public class TestLockInterruptibly {

    static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {

    Thread thread1 = new Thread(new Runnable() {

    @Override

    public void run() {

    try {

    doPrint("thread 1 get lock.");

    do123();

    doPrint("thread 1 end.");

    } catch (InterruptedException e) {

    doPrint("thread 1 is interrupted.");

    }

    }

    });

    Thread thread2 = new Thread(new Runnable() {

    @Override

    public void run() {

    try {

    doPrint("thread 2 get lock.");

    do123();

    doPrint("thread 2 end.");

    } catch (InterruptedException e) {

    doPrint("thread 2 is interrupted.");

    }

    }

    });

    thread1.setName("thread1");

    thread2.setName("thread2");

    thread1.start();

    try {

    Thread.sleep(100);//等待一会使得thread1会在thread2前面执行

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    thread2.start();

    }

    private static void do123() throws InterruptedException {

    lock.lockInterruptibly();

    doPrint(Thread.currentThread().getName() + " is locked.");

    try {

    doPrint(Thread.currentThread().getName() + " doSoming1....");

    Thread.sleep(5000);//等待几秒方便查看线程的先后顺序

    doPrint(Thread.currentThread().getName() + " doSoming2....");

    doPrint(Thread.currentThread().getName() + " is finished.");

    } finally {

    lock.unlock();

    }

    }

    private static void doPrint(String text) {

    System.out.println((new Date()).toLocaleString() + " : " + text);

    }

    }

    上面代码中有两个线程,thread1比thread2更早启动,为了能看到拿锁的过程将上锁的代码sleep了5秒钟,这样就可以感受到前后两个线程进入获取锁的过程。最终上面的代码运行结果如下:

    2016-9-28 15:12:56 : thread 1 get lock.

    2016-9-28 15:12:56 : thread1 is locked.

    2016-9-28 15:12:56 : thread1 doSoming1....

    2016-9-28 15:12:56 : thread 2 get lock.

    2016-9-28 15:13:01 : thread1 doSoming2....

    2016-9-28 15:13:01 : thread1 is finished.

    2016-9-28 15:13:01 : thread1 is unloaded.

    2016-9-28 15:13:01 : thread2 is locked.

    2016-9-28 15:13:01 : thread2 doSoming1....

    2016-9-28 15:13:01 : thread 1 end.

    2016-9-28 15:13:06 : thread2 doSoming2....

    2016-9-28 15:13:06 : thread2 is finished.

    2016-9-28 15:13:06 : thread2 is unloaded.

    2016-9-28 15:13:06 : thread 2 end.

    可以看到,thread1先获得锁,一会thread2也来拿锁,但这个时候thread1已经占用了,所以thread2一直到thread1释放了锁后才拿到锁。

    **这段代码说明lockInterruptibly后面来获取锁的线程需要等待前面的锁释放了才能获得锁。**但这里还没有体现出可中断的特点,为此增加一些代码:

    thread2.start();

    try {

    Thread.sleep(1000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    //1秒后把线程2中断

    thread2.interrupt();

    在thread2启动后调用一下thread2的中断方法,好吧,先跑一下代码看看结果:

    2016-9-28 15:16:46 : thread 1 get lock.

    2016-9-28 15:16:46 : thread1 is locked.

    2016-9-28 15:16:46 : thread1 doSoming1....

    2016-9-28 15:16:46 : thread 2 get lock.

    2016-9-28 15:16:47 : thread 2 is interrupted.

    2016-9-28 15:16:51 : thread1 doSoming2....

    2016-9-28 15:16:51 : thread1 is finished.

    2016-9-28 15:16:51 : thread1 is unloaded.

    2016-9-28 15:16:51 : thread 1 end.

    和前面的代码相比可以发现,thread2正在等待thread1释放锁,但是这时thread2自己中断了,thread2后面的代码则不会再继续执行。

    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();

    }

    在JDK里有个ReentrantReadWriteLock实现,就是可重入的读-写锁。ReentrantReadWriteLock可以构造为公平的或者非公平的两种类型。如果在构造时不显式指定则会默认的创建非公平锁。在非公平锁的模式下,线程访问的顺序是不确定的,就是可以闯入;可以由写者降级为读者,但是读者不能升级为写者。

    如果是公平锁模式,那么选择权交给等待时间最长的线程,如果一个读线程获得锁,此时一个写线程请求写入锁,那么就不再接收读锁的获取,直到写入操作完成。

    简单的代码分析 在ReentrantReadWriteLock里其实维护的是一个sync的锁,只是看起来语义上像是一个读锁和写锁。看一下它的构造函数:

    public ReentrantReadWriteLock(boolean fair) {

    sync = fair ? new FairSync() : new NonfairSync();

    readerLock = new ReadLock(this);

    writerLock = new WriteLock(this);

    }

    //读锁的构造函数

    protected ReadLock(ReentrantReadWriteLock lock) {

    sync = lock.sync;

    }

    //写锁的构造函数

    protected WriteLock(ReentrantReadWriteLock lock) {

    sync = lock.sync;

    }

    可以看到实际上读/写锁在构造时都是引用的ReentrantReadWriteLock的sync锁对象。而这个Sync类是ReentrantReadWriteLock的一个内部类。总之读/写锁都是通过Sync来完成的。它是如何来协作这两者关系呢?

    //读锁的加锁方法

    public void lock() {

    sync.acquireShared(1);

    }

    //写锁的加锁方法

    public void lock() {

    sync.acquire(1);

    }

    区别主要是读锁获得的是共享锁,而写锁获取的是独占锁。这里有个点可以提一下,就是ReentrantReadWriteLock为了保证可重入性,共享锁和独占锁都必须支持持有计数和重入数。而ReentrantLock是使用state来存储的,而state只能存一个整形值,为了兼容两个锁的问题,所以将其划分了高16位和低16位分别存共享锁的线程数量或独占锁的线程数量或者重入计数。

    其他

    写了一大篇感觉要写下去篇幅太长了,还有一些比较有用的锁:

    CountDownLatch

    就是设置一个同时持有的计数器,而调用者调用CountDownLatch的await方法时如果当前的计数器不为0就会阻塞,调用CountDownLatch的release方法可以减少计数,直到计数为0时调用了await的调用者会解除阻塞。

    Semaphone

    信号量是一种通过授权许可的形式,比如设置100个许可证,这样就可以同时有100个线程同时持有锁,如果超过这个量后就会返回失败。

    感谢阅读此文,希望能帮助到大家,谢谢大家对本站的支持!

    展开全文
  • 前言上篇文章写了synchronized的升级,本文主要来写下java中的降级,其实在很多中和博客里的观点都是没有降级这一说的,但是java中确实有锁降级的场景。降级降级发生在读写中,写降级读的过程。...

    前言

    上篇文章写了synchronized的锁升级,本文主要来写下java中的锁降级,其实在很多书中和博客里的观点都是没有锁降级这一说的,但是java中确实有锁降级的场景。

    锁降级

    锁降级发生在读写锁中,写锁降级读锁的过程。

    读写锁ReentrantReadWriteLock

    读写锁,既可以获取读锁,也可以获取写锁

    需要明确的是:写锁是独占锁,所谓独占即为独自占有,别的线程既不能获取到该锁的写锁,也不能获取到对应的读锁。

    读锁是共享锁,所谓共享即是所有线程都可以共同持有该读锁。

    两个重点:当一个线程获取了写锁(未释放),其他线程既不可以获取到写锁,也不能获取到读锁。

    当一个线程获取到了读锁(未释放),其他线程可以获取到读锁,但是不能获取到写锁。

    代码验证

    1/**

    2 *

    3 * @author wengyz

    4 * @version ReadAndWriteLockDemo.java, v 0.1 2020-05-05 15:15

    5 */

    6public class ReadAndWriteLockDemo {

    7 public static void main(String[] args) {

    8 // 读写锁

    9 ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    10 // 读锁

    11 ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();

    12 // 写锁

    13 ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();

    14

    15 /**

    16 * 写锁一

    17 */

    18 new Thread(new Runnable() {

    19 @Override

    20 public void run() {

    21 writeLock.lock();

    22 System.out.println("写锁一加锁成功");

    23 writeLock.unlock();

    24 }

    25 }).start();

    26

    27 /**

    28 * 读锁一

    29 */

    30 new Thread(new Runnable() {

    31 @Override

    32 public void run() {

    33 readLock.lock();

    34 System.out.println("读锁一加锁成功");

    35 readLock.unlock();

    36 }

    37 }).start();

    38

    39 /**

    40 * 读锁二

    41 */

    42 new Thread(new Runnable() {

    43 @Override

    44 public void run() {

    45 readLock.lock();

    46 System.out.println("读锁二加锁成功");

    47 readLock.unlock();

    48 }

    49 }).start();

    50

    51 /**

    52 * 写锁二

    53 */

    54 new Thread(new Runnable() {

    55 @Override

    56 public void run() {

    57 writeLock.lock();

    58 System.out.println("写锁二加锁成功");

    59 writeLock.unlock();

    60 }

    61 }).start();

    62 }

    63}

    结论:如果写锁一不释放,下面的读锁一、二和写锁二都将获取不到锁,如果读锁一不释放,读锁二可以获取到锁,但是写锁二将会获取不到锁。

    这样设计的目的,是为了保证读取的数据的不存在脏数据。

    锁降级分析

    锁降级指的是写锁降级为读锁的过程,他的过程是持有写锁,获取读锁,然后释放写锁。

    注意以下情况不是锁降级

    这种情况可以理解成是锁竞争,而且此种做法是错误的,不能保证再次获取读锁的时候没有写锁曾经占有过,即不能保证数据的可见性。

    锁降级的本质是释放掉独占锁,使其他线程可以获取到读锁,提高并发,而当前线程持有读锁来保证数据的可见性。

    最后

    这个问题是上次面试一个小伙子时,他没有回答好的问题,其实他能知道读写锁的降级已经很不错了,说明私下是有看点东西的,但是确实没有深入的研究过,今天是五一假期的最后一天索性写篇文章分享下。

    来源公众号:有意思的程序员

    展开全文
  • 读书笔记部分内容来源出版,版权归本书作者,如有错误,请指正。欢迎star、fork,读书笔记系列会同步更新gitmodulej360-jdk-thread/me.j360.jdk.concurrent本书前三章分别为并发编程的挑战,也就是并发编程的...

    读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。

    欢迎star、fork,读书笔记系列会同步更新

    git

    module

    j360-jdk-thread/me.j360.jdk.concurrent

    本书前三章分别为

    并发编程的挑战,也就是并发编程的缘由所在

    底层的实现原理

    java内存模型

    分别从cpu x86,x64以及内存模型等概念中描述java对并发编程的实现和控制,概念较为底层和基础,读书笔记略过前三章直接从第四章应用实现及原理基础开始。

    章节

    并发编程基础

    java中的锁

    并发容器和框架(重点)

    13个操作原子类

    java并发工具类

    线程池

    Execurot框架

    内容

    java中的锁

    Lock接口

    Lock接口出现之前,java是通过synchronized关键字实现的锁功能,javase5之后,并发包新增了Lock接口

    Lock使用方式,和分布式锁的构造很像。

    Lock lock = new ReentrantLock

    lock.lock();

    try{

    }finally{

    lock.unlock();

    }

    Lock接口提供了Synchronized关键字不具备的特性

    尝试非阻塞地获取锁

    当前线程尝试获取锁,没有其他线程获取锁,则成功

    能被中断的获取锁

    超时获取锁

    在指定的时间内获取锁

    Lock接口的API

    void lock()

    void lockInterruptibly() throws InterruptedException

    boolean tryLock()

    boolean tryLock(long time,TimeUtil unit) throws InterruptedException

    void unlock()

    Condition newCondition

    获取等待通知组件,该组件和当前的锁绑定,当前线程只有获取了锁,才能调用该组件的wait()方法,而调用后,当前线程将会释放锁

    队列同步器

    锁的实现基于队列同步器完成,AbstractQueuedSynchronized(简称同步器),使用一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作

    public class ReentrantLock implements Lock, java.io.Serializable {

    private static final long serialVersionUID = 7373984872572414699L;

    /** Synchronizer providing all implementation mechanics */

    private final Sync sync;

    /**

    * Base of synchronization control for this lock. Subclassed

    * into fair and nonfair versions below. Uses AQS state to

    * represent the number of holds on the lock.

    在这里!!!

    */

    abstract static class Sync extends AbstractQueuedSynchronizer {

    private static final long serialVersionUID = -5179523762034025860L;

    /**

    * Performs {@link Lock#lock}. The main reason for subclassing

    * is to allow fast path for nonfair version.

    */

    abstract void lock();

    /**

    * Performs non-fair tryLock.  tryAcquire is

    * implemented in subclasses, but both need nonfair

    * try for trylock method.

    */

    final boolean nonfairTryAcquire(int acquires) {

    final Thread current = Thread.currentThread();

    int c = getState();

    if (c == 0) {

    if (compareAndSetState(0, acquires)) {

    setExclusiveOwnerThread(current);

    return true;

    }

    }

    else if (current == getExclusiveOwnerThread()) {

    int nextc = c + acquires;

    if (nextc 

    throw new Error("Maximum lock count exceeded");

    setState(nextc);

    return true;

    }

    return false;

    }

    protected final boolean tryRelease(int releases) {

    int c = getState() - releases;

    if (Thread.currentThread() != getExclusiveOwnerThread())

    throw new IllegalMonitorStateException();

    boolean free = false;

    if (c == 0) {

    free = true;

    setExclusiveOwnerThread(null);

    }

    setState(c);

    return free;

    }

    protected final boolean isHeldExclusively() {

    // While we must in general read state before owner,

    // we don't need to do so to check if current thread is owner

    return getExclusiveOwnerThread() == Thread.currentThread();

    }

    final ConditionObject newCondition() {

    return new ConditionObject();

    }

    // Methods relayed from outer class

    final Thread getOwner() {

    return getState() == 0 ? null : getExclusiveOwnerThread();

    }

    final int getHoldCount() {

    return isHeldExclusively() ? getState() : 0;

    }

    final boolean isLocked() {

    return getState() != 0;

    }

    /**

    * Reconstitutes this lock instance from a stream.

    * @param s the stream

    */

    private void readObject(java.io.ObjectInputStream s)

    throws java.io.IOException, ClassNotFoundException {

    s.defaultReadObject();

    setState(0); // reset to unlocked state

    }

    }

    /**

    * Sync object for non-fair locks

    非公平锁

    */

    static final class NonfairSync extends Sync {

    private static final long serialVersionUID = 7316153563782823691L;

    /**

    * Performs lock.  Try immediate barge, backing up to normal

    * acquire on failure.

    */

    final void lock() {

    if (compareAndSetState(0, 1))

    setExclusiveOwnerThread(Thread.currentThread());

    else

    acquire(1);

    }

    protected final boolean tryAcquire(int acquires) {

    return nonfairTryAcquire(acquires);

    }

    }

    /**

    * Sync object for fair locks

    公平锁

    */

    static final class FairSync extends Sync {

    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {

    acquire(1);

    }

    /**

    * Fair version of tryAcquire.  Don't grant access unless

    * recursive call or no waiters or is first.

    */

    protected final boolean tryAcquire(int acquires) {

    final Thread current = Thread.currentThread();

    int c = getState();

    if (c == 0) {

    if (!hasQueuedPredecessors() &&

    compareAndSetState(0, acquires)) {

    setExclusiveOwnerThread(current);

    return true;

    }

    }

    else if (current == getExclusiveOwnerThread()) {

    int nextc = c + acquires;

    if (nextc 

    throw new Error("Maximum lock count exceeded");

    setState(nextc);

    return true;

    }

    return false;

    }

    }

    重入锁 ReetrentLock

    支持重进入的锁,能够支持一个线程对资源的重复加锁,代码见上面。

    读写锁 ReetrentReadWriteLock

    特性

    公平性选择

    重进入

    锁降级

    接口示例

    int getReadLockCount()

    读锁被或许的次数

    int getReadHoldCount()

    当前线程或许读锁的次数

    int getWriteLockCount()

    int getWriteHoldCount()

    通过Cache来解释读写锁,HashMap是非线程安全的,通过读写锁实现Cache的线程安全

    public class Cache {

    static Map map = new HashMap();

    static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

    static Lock r = rwl.readLock();

    static Lock w = rwl.writeLock();

    public static final Object get(String key){

    r.lock();

    try {

    return map.get(key);

    }finally {

    r.unlock();

    }

    }

    public static final Object put(String key,Object value){

    w.lock();

    try {

    return map.put(key,value);

    }finally {

    w.unlock();

    }

    }

    public static final void clear() {

    w.lock();

    try {

    map.clear();

    }finally {

    w.unlock();

    }

    }

    }

    Condition接口和示例

    public class ConditionUseCase {

    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    public static void main(String[] args){

    }

    public void conditionWait() throws InterruptedException {

    lock.lock();

    try {

    condition.await();

    }finally {

    lock.unlock();

    }

    }

    public void conditionSignal(){

    lock.lock();

    try {

    condition.signal();

    }finally {

    lock.unlock();

    }

    }

    }

    部分方法描述

    void await()

    当前线程进入等待状态,直到被通知或中断

    void awaitUninterruptibly()

    当前线程进入等待状态,对中断不敏感

    long awaitNanos(long nanosTimeout)

    当前线程进入等待状态,直到被通知,中断或者超时,返回值表示剩余的时间,返回值如果是0或者负数,那么可以认定已经超时了

    boolean awaitUntil(Date deadline)

    当前线程进入等待状态,直到被通知、中断或者到某个时间,如果没有到指定时间,返回true,否则到了指定时间,返回false

    void signal()

    唤醒一个等待在condition中的线程,该线程从等待方法返回前必须获取与Condition相关联的锁

    void signlAll()

    唤醒所有等待的condition中的线程,能够从等待方法返回的线程必须获得与condition相关联的锁

    有界队列BoundedQueue解释Condition

    public class BoundedQueue {

    private Object[] items;

    private int addIndex,removeIndex,count;

    private Lock lock = new ReentrantLock();

    private Condition notEmpty = lock.newCondition();

    private Condition notFull = lock.newCondition();

    public BoundedQueue(int size){

    items = new Object[size];

    }

    public void add(T t) throws InterruptedException {

    lock.lock();

    try {

    while(count == items.length)

    notFull.await();

    items[addIndex] = t;

    if(++addIndex == items.length)

    addIndex = 0;

    ++count;

    notEmpty.signal();

    }finally {

    lock.unlock();

    }

    }

    public T remove() throws InterruptedException {

    lock.lock();

    try {

    while(count == 0)

    notEmpty.await();

    Object x = items[removeIndex];

    if(++removeIndex == items.length)

    removeIndex = 0;

    --count;

    notFull.signal();

    return (T) x;

    }finally {

    lock.unlock();

    }

    }

    }

    展开全文
  • Java并发编程的艺术》读书笔记 第五章 Java中的 文章目录《Java并发编程的艺术》读书笔记 第五章 Java中的1.Lock接口2.队列同步器3.重入4.读写5.LockSupport工具6.Condition接口6.1 Condition的实现分析 1...
  • 今天正在学习一本新书 其中当我看到synchronized关键字的片段的时候就在想一个问题其中中说到一个对象会自动生成唯一一个对象的,就相当于一个对象只有一把属于自己的和钥匙,如果出现下面情况public class ...
  • 前言之前只是对Java各种锁都有所认识,但没有一个统一的整理及总结,且没有对“锁升级”这一概念的加深理解,今天趁着...一、Java锁的分类及简单介绍平时大家都知道的锁一般都有:CAS锁,synchronized锁,Reentra...
  • Java的概念 自旋:为了不放弃CPU执行时间,循环的使用CAS技术对数据进行尝试更新,直至成功。 悲观:假定会发生并发冲突,同步所有共享数据的相关操作,从读书据就开始上。 乐观:假定没有冲突,在...
  • 在阅读《java nio》这本书的时候,这本书中提到,FileLock 是进程级的。在线程级上:如果一个线程在某个文件上获得了一个独占,然后第二个线程利用一个单独打开的通道来请求该文件的独占,那么第二个线程的请求...
  • 【并发】Java锁分类

    2019-03-13 14:43:55
    Java并发领域,根据的状态、特性、粒度、轻重等有不同的分类方法,很多并发书籍或者文章都会或多或少提及一些专有的名词,为了理解各种分类的分类方法、原理及作用,在此特意总结一下。 一、乐观/悲观 ...
  • 一、概述 本篇文章是基于《深入理解Java虚拟机》一的读书笔记
  • 5.3 重入 重入(ReentrantLock)就是支持重进入的,它表示该能够支持一个线程对资源的重复加锁。 5.3.1 实现重进入 线程再次获取需要去识别获取的线程是否为当前占据的线程,如果是,则再次...
  • 最近在看《Java并发编程实战》,关于java锁的可重入性,中有如下代码片段:public class Widget {public synchronized void doSomething() {...}}public class LoggingWidget extends Widget {public synchronized...
  • 自旋释义: 请求的线程(假设为线程A)再未获得的时候,不进入阻塞状态,而是让它「再执行一会」即占用CPU一会,看看持有的线程是否很快释放资源。但是为了让这个线程A进入「等待」的状态,需要让它执行...
  • 前言之前只是对Java各种锁都有所认识,但没有一个统一的整理及总结,且没有对“锁升级”这一概念的加深理解,今天趁着...一、Java锁的分类及简单介绍平时大家都知道的锁一般都有:CAS锁,synchronized锁,Reentra...
  • 在公平的上,线程将按照它们发出请求的顺序来获得,但在非公平的上,则允许插队:当一个线程请求非公平的时,如果发出请求的同时该的状态变为可用,那么线程将跳过队列中所有的等待线程并获得这个。...
  • 看编程思想一,也写了段代码,发现对static的资源加锁无效,左思右想终于找出了原因,现贴出与大家分享。import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;class Source{int i...
  • Java优化

    2018-04-21 18:10:21
    最近在学习jvm,在看到周志明老师的深入理解Java虚拟机这本书的同时,将自己的所学记录到博客,与大家一起分享。 在jdk1.6中加入大量的优化技术,比如适应性自旋消除,粗化,轻量级,偏向等等,这些...
  • 文章目录5.1 Lock接口5.2队列同步器Node结点队列同步器的实现独占式同步状态获取与释放自旋过程释放同步状态共享式同步状态获取与释放独占式超时获取同步状态5.3重入5.4读写5.5LockSupport工具5.6Condition接口 ...
  • 一、Lock接口: 在Lock接口出现之前,Java靠synchronized关键字实现的功能。但是synchronized关键字将的获取与释放固化了,显得并没有这么灵活。在jdk1.5以后,新增加了Lock接口。lock主要具备synchronized...
  • Java并发编程的艺术》 读书笔记: - - 原作者:方腾飞 ...Java并发编程中最重要的同步机制。除了让临界区互斥执行外,还可以让释放的线程向获取同一个的线程发送消息。 下面是释放-获取的示例...
  • java锁竞争检测的疑问

    2015-04-09 07:34:39
    看优化的,上面提到现在JVM对锁进行了优化,有锁竞争,JVM底层先是自旋,再调用底层操作系统的,然后现在用看主动上下文切换,和被动上线文切换的差/NCPU*80000/频率 看是否大于百分之5,来判断是否有锁。...
  • java中的

    2016-11-24 10:11:54
    java中有哪些 这个问题在我看了一遍后尽然无法回答,说明自己对于的概念了解的不够。于是再次翻看了一下里的内容,突然有点打开脑门的感觉。看来确实是要学习的最好方式是要带着问题去学,并且解决问题。 在...
  • 最近在看Java Concurrent in Practice(java并发编程实践),发现自己对java的线程、等机制,理解很肤浅,学习的也不够全面。打算借着这本书,全面的学习下JDK的并发包和一些线程相关的理论知识,填补自己的空白,...
  • Lock 提供一种无条件的、可轮询的、定时的、可中断的获取操作,全部加锁和解锁的方法都是显式的。 public interface Lock { void lock(); // 获取。 void lockInterruptibly() throws ...
  • 1:Lock接口出现之前,Java主要靠synchronized关键字实现功能。它提供了显式地获取和释放的便捷性,但是也缺少了synchronized的隐式获取和释放的便捷性,程序员可以在不同场景进行选择。 Lock的使用也很简单...
  • Java并发实现的三个特征:原子性、可见性、顺序性。原子性操作的实现原理 (1)总线实现原子性:所谓的总线锁定就是使用处理器提供的一个LOCK#信号,当处理器在总线上输出此信号的时候,其他处理器的请求将会被...
  • 线程安全的定义 线程的安全等级 线程安全的实现方法 互斥同步 非阻塞同步 无同步方案 ...优化 ...自旋与自适应自旋 消除 粗化 轻量级 偏向 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,252
精华内容 500
关键字:

java锁书

java 订阅