精华内容
参与话题
问答
  • Java 中的Lock锁

    千次阅读 2019-05-26 11:06:01
    Lock锁,可以得到和synchronized一样的效果,即实现原子性、有序性和可见性。 相较于synchronized,Lock锁可手动获取锁和释放锁、可中断的获取锁、超时获取锁。 Lock是一个接口,两个直接实现类:ReentrantLock...

    Lock锁,可以得到和 synchronized一样的效果,即实现原子性、有序性和可见性。

    相较于synchronized,Lock锁可手动获取锁和释放锁、可中断的获取锁、超时获取锁。

    Lock 是一个接口,两个直接实现类:ReentrantLock(重入锁), ReentrantReadWriteLock(读写锁)。

    1. 概述

    Lock锁,使用时手动获取锁和释放锁,比synchronized更加灵活;可中断的获取锁;超时获取锁。

    Lock 锁的基本用法, l.lock()方法进行上锁, l.unlock()方法进行解锁,如下所示。

     Lock l = ...;
     l.lock(); // 上锁
     try {
       // access the resource protected by this lock
     } finally {
       l.unlock(); // 解锁
     }

    2. Lock锁的API

    修饰符和类型 方法 描述
    void lock() 获得锁。
    void lockInterruptibly​() 获得锁,可中断。举个例子,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
    boolean tryLock​() 锁在空闲的才能获取锁(未获得锁不会等待)。举个例子:当两个线程同时通过lock.trylock()想获取某个锁时,假若此时线程A获取到了锁,而线程B不会等待,直接放弃获取锁。
    boolean tryLock​(long time, TimeUnit unit)

    如果锁定可用,则此方法立即返回值true

    如果锁不可用,则当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态:

    • 当前线程获取锁。
    • 其他一些线程中断当前线程。
    • 等待时间过去了,返回false
    void unlock​() 释放锁。

    3. lock() 方法,unlock()方法

    创建一个 Lock 锁:

    Lock l = new ReentrantLock();

    给代码块上锁:

            l.lock();
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + ": ");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            l.unlock();

    全部代码:

    package lock;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class MyLockStudy implements Runnable {
    
        private int count;
        Lock l = new ReentrantLock();
    
        @Override
        public void run() {
            l.lock();
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + ": ");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            l.unlock();
        }
    
        public static void main(String args[]) {
            MyLockStudy runn = new MyLockStudy();
            Thread thread1 = new Thread(runn, "thread1");
            Thread thread2 = new Thread(runn, "thread2");
            Thread thread3 = new Thread(runn, "thread3");
            thread1.start();
            thread2.start();
            thread3.start();
        }
    }
    

    运行截图:

    4. tryLock()方法

    tryLock​(),锁在空闲的才能获取锁(未获得锁不会等待,lock()未获得锁会进入阻塞状态 ),返回值为boolean类型,获取锁则为 true ,反之为 false。

    tryLock() 进行加锁:

            if (l.tryLock()) {
                System.out.println(Thread.currentThread().getName() + "获取锁");
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + ": ");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                l.unlock();
            } else {
                System.out.println(Thread.currentThread().getName() + "未获取锁");
            }

    完整代码: 

    package lock;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class MyLockStudy implements Runnable {
    
        private int count;
        Lock l = new ReentrantLock();
    
        @Override
        public void run() {
            if (l.tryLock()) {
                System.out.println(Thread.currentThread().getName() + "获取锁");
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + ": ");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                l.unlock();
            } else {
                System.out.println(Thread.currentThread().getName() + "未获取锁");
            }
    
        }
    
        public static void main(String args[]) {
            MyLockStudy runn = new MyLockStudy();
            Thread thread1 = new Thread(runn, "thread1");
            Thread thread2 = new Thread(runn, "thread2");
            Thread thread3 = new Thread(runn, "thread3");
            thread1.start();
            thread2.start();
            thread3.start();
        }
    }
    

    运行截图如下所示:

    5. tryLock​(long time, TimeUnit unit) 方法

    如果锁定可用,则此方法立即返回值true。如果锁不可用,则当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态:

    • 当前线程获取锁。
    • 其他一些线程中断当前线程。
    • 等待时间过去了,返回false

    设置等待时间为 1000 毫秒。

            try {
                if (l.tryLock(1000, TimeUnit.MILLISECONDS)) {
                    System.out.println(Thread.currentThread().getName() + "获取锁");
                    for (int i = 0; i < 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ": ");
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    l.unlock();
                } else {
                    System.out.println(Thread.currentThread().getName() + "未获取锁");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    

    完整代码如下所示:

    package lock;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class MyLockStudy implements Runnable {
    
        private int count;
        Lock l = new ReentrantLock();
    
        @Override
        public void run() {
            try {
                if (l.tryLock(1000, TimeUnit.MILLISECONDS)) {
                    System.out.println(Thread.currentThread().getName() + "获取锁");
                    for (int i = 0; i < 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ": ");
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    l.unlock();
                } else {
                    System.out.println(Thread.currentThread().getName() + "未获取锁");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    
        public static void main(String args[]) {
            MyLockStudy runn = new MyLockStudy();
            Thread thread1 = new Thread(runn, "thread1");
            Thread thread2 = new Thread(runn, "thread2");
            Thread thread3 = new Thread(runn, "thread3");
            thread1.start();
            thread2.start();
            thread3.start();
        }
    }
    

    运行截图:

    如果锁不可用,则当前线程将被禁用以进行线程调度,并且在发生以下三种情况之一之前处于休眠状态,当前线程是可以被中断的。

    package lock;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class MyLockStudy implements Runnable {
    
        private int count;
        Lock l = new ReentrantLock();
    
        @Override
        public void run() {
            try {
                if (l.tryLock(1000, TimeUnit.MILLISECONDS)) {
                    System.out.println(Thread.currentThread().getName() + "获取锁");
                    for (int i = 0; i <= 500000000; i++) {
                        if (i == 500000000) {
                            System.out.println("运算结束");
                        }
                    }
                    l.unlock();
                } else {
                    System.out.println(Thread.currentThread().getName() + "未获取锁");
                }
            } catch (InterruptedException e) {
                System.out.println("中断");
                e.printStackTrace();
            }
    
        }
    
        public static void main(String args[]) {
            MyLockStudy runn = new MyLockStudy();
            Thread thread1 = new Thread(runn, "thread1");
            Thread thread2 = new Thread(runn, "thread2");
            Thread thread3 = new Thread(runn, "thread3");
            thread1.start();
            thread2.start();
            thread3.start();
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            thread1.interrupt();
            thread2.interrupt();
            thread3.interrupt();
        }
    }
    

    线程1获取了锁,其他线程休眠,然后被中断。 

     

    展开全文
  • synchronize和Lock锁的区别

    万次阅读 2018-03-28 23:32:03
    为什么java已经通过synchronized关键字实现同步访问了,还需要提供Lock? synchronized的缺陷 前面博客有提到过释放对象的有两种情况: 程序执行完同步代码块会释放代码块。 程序在执行同步代码块是出现异常...

    为什么java已经通过synchronized关键字实现同步访问了,还需要提供Lock?

    synchronized的缺陷

    前面博客有提到过释放对象的锁有两种情况:

    • 程序执行完同步代码块会释放代码块。
    • 程序在执行同步代码块是出现异常,JVM会自动释放锁去处理异常。

    如果获取锁的线程需要等待I/O或者调用了sleep()方法被阻塞了,但仍持有锁,其他线程只能干巴巴的等着,这样就会很影响程序效率。
    因此就需要一种机制,可以不让等待的线程已知等待下去,比如值等待一段时间或响应中断,Lock锁就可以办到。

    再举个例子:当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。但是采用synchronized关键字来实现同步的话,就会导致一个问题:如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。
    因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。
    另外,Lock可以知道线程有没有得到锁,而synchronized不能。

    总结区别

    总结来说,Lock与synchronized有以下区别:

    1. Lock是一个接口,而synchronized是关键字。
    2. synchronized会自动释放锁,而Lock必须手动释放锁。
    3. Lock可以让等待锁的线程响应中断,而synchronized不会,线程会一直等待下去。
    4. 通过Lock可以知道线程有没有拿到锁,而synchronized不能。
    5. Lock能提高多个线程读操作的效率。
    6. synchronized能锁住类、方法和代码块,而Lock是块范围内的
    展开全文
  • Lock锁底层原理

    万次阅读 多人点赞 2019-01-07 01:28:04
    一种是关键字:synchronized,一种是concurrent包下的lock锁。 synchronized是java底层支持的,而concurrent包则是jdk实现。   关于synchronized的原理可以阅读再有人问你synchronized是什么,就把这篇文章...

    当多个线程需要访问某个公共资源的时候,我们知道需要通过加锁来保证资源的访问不会出问题。java提供了两种方式来加锁

    一种是关键字:synchronized,一种是concurrent包下的lock锁。

    synchronized是java底层支持的,而concurrent包则是jdk实现

     

    关于synchronized的原理可以阅读再有人问你synchronized是什么,就把这篇文章发给他。

    在这里,我会用尽可能少的代码,尽可能轻松的文字,尽可能多的图来看看lock的原理。

    我们以ReentrantLock为例做分析,其他原理类似。

    我把这个过程比喻成一个做菜的过程,有什么菜,做法如何?

    我先列出lock实现过程中的几个关键词:计数值、双向链表、CAS+自旋

     

    我们以ReentrantLock为例做分析,其他原理类似。

        可以实现公平锁和非公平锁( 当有线程竞争锁时,当前线程会首先尝试获得锁而不是在队列中进行排队等候,这对于那些已经在队列中排队的线程来说显得不公平,这也是非公平锁的由来),默认情况下为非公平锁。

    实现原理

    ReentrantLock() 干了啥

      public ReentrantLock() {
    
            sync = new NonfairSync();
    
        }

    在lock的构造函数中,定义了一个NonFairSync,

    static final class NonfairSync extends Sync

    NonfairSync 又是继承于Sync

    abstract static class Sync extends AbstractQueuedSynchronizer

     

    一步一步往上找,找到了

    这个鬼AbstractQueuedSynchronizer(简称AQS),最后这个鬼,又是继承于AbstractOwnableSynchronizer(AOS),AOS主要是保存获取当前锁的线程对象,代码不多不再展开。最后我们可以看到几个主要类的继承关系:

                                                

        FairSync 与 NonfairSync的区别在于,是不是保证获取锁的公平性,因为默认是NonfairSync(非公平性),我们以这个为例了解其背后的原理。

    其他几个类代码不多,最后的主要代码都是在AQS中,我们先看看这个类的主体结构。

    看看AbstractQueuedSynchronizer是个什么

    再看看Node是什么?

    看到这里的同学,是不是有种热泪盈眶的感觉,这尼玛,不就是双向链表么?我还记得第一次写这个数据结构的时候,发现居然还有这么神奇的一个东西。

    最后我们可以发现锁的存储结构就两个东西:"双向链表" + "int类型状态"。

    简单来说,ReenTrantLock的实现是一种自旋锁,通过循环调用CAS操作来实现加锁。它的性能比较好也是因为避免了使线程进入内核态的阻塞状态。想尽办法避免线程进入内核的阻塞状态是我们去分析和理解锁设计的关键钥匙。

    需要注意的是,他们的变量都被"transientvolatile修饰。

     

    一个int值,一个双向链表是如何烹饪处理锁这道菜的呢,Doug Lea大神就是大神,

    我们接下来看看,如何获取锁?

     

    lock.lock()怎么获取锁?

    public void lock() {
    
        sync.lock();
    
    }

    可以看到调用的是,NonfairSync.lock()

    看到这里,我们基本有了一个大概的了解,还记得之前AQS中的int类型的state值,

    这里就是通过CAS(乐观锁)去修改state的值(锁状态值)。lock的基本操作还是通过乐观锁来实现的。

    获取锁通过CAS,那么没有获取到锁,等待获取锁是如何实现的?我们可以看一下else分支的逻辑,acquire方法

    public final void acquire(int arg) {
    
        if (!tryAcquire(arg) &&
    
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    
            selfInterrupt();
    
    }

    这里干了三件事情:

    • tryAcquire:会尝试再次通过CAS获取一次锁。

    • addWaiter:将当前线程加入上面锁的双向链表(等待队列)中

    • acquireQueued:通过自旋,判断当前队列节点是否可以获取锁。

     

    addWaiter() 添加当前线程到等待链表中

    可以看到,通过CAS确保能够在线程安全的情况下,将当前线程加入到链表的尾部。

    enq是个自旋+上述逻辑,有兴趣的可以翻翻源码。

     

    acquireQueued()    自旋+CAS尝试获取锁

    可以看到,当当前线程到头部的时候,尝试CAS更新锁状态,如果更新成功表示该等待线程获取成功。从头部移除。

     

    每一个线程都在 自旋+CAS

    最后简要概括一下,获取锁的一个流程

     

    lock.unlock() 释放锁

    public void unlock() {
    
        sync.release(1);
    
    }

    可以看到调用的是,NonfairSync.release()

    最后又调用了NonfairSync.tryRelease()

    基本可以确认,释放锁就是对AQS中的状态值State进行修改。同时更新下一个链表中的线程等待节点

     

     

    总结

    • lock的存储结构:一个int类型状态值(用于锁的状态变更),一个双向链表(用于存储等待中的线程)
    • lock获取锁的过程:本质上是通过CAS来获取状态值修改,如果当场没获取到,会将该线程放在线程等待链表中。
    • lock释放锁的过程:修改状态值,调整等待链表。

    可以看到在整个实现过程中,lock大量使用CAS+自旋。因此根据CAS特性,lock建议使用在低锁冲突的情况下。目前java1.6以后,官方对synchronized做了大量的锁优化(偏向锁、自旋、轻量级锁)。因此在非必要的情况下,建议使用synchronized做同步操作。

    最后,希望我的分析,能对你理解锁的实现有所帮助。

    ____________________________________________________________________________

    锁实现

        简单说来,AbstractQueuedSynchronizer会把所有的请求线程构成一个CLH队列,当一个线程执行完毕(lock.unlock())时会激活自己的后继节点,但正在执行的线程并不在队列中,而那些等待执行的线程全 部处于阻塞状态,经过调查线程的显式阻塞是通过调用LockSupport.park()完成,而LockSupport.park()则调用 sun.misc.Unsafe.park()本地方法,再进一步,HotSpot在Linux中中通过调用pthread_mutex_lock函数把 线程交给系统内核进行阻塞。

        与synchronized相同的是,这也是一个虚拟队列,不存在队列实例,仅存在节点之间的前后关系。令人疑惑的是为什么采用CLH队列呢?原生的CLH队列是用于自旋锁,但Doug Lea把其改造为阻塞锁

        当有线程竞争锁时,该线程会首先尝试获得锁,这对于那些已经在队列中排队的线程来说显得不公平,这也是非公平锁的由来,与synchronized实现类似,这样会极大提高吞吐量。 如果已经存在Running线程,则新的竞争线程会被追加到队尾,具体是采用基于CAS的Lock-Free算法,因为线程并发对Tail调用CAS可能会 导致其他线程CAS失败,解决办法是循环CAS直至成功。AQS的实现非常精巧,令人叹为观止,不入细节难以完全领会其精髓,下面详细说明实现过程:

        AbstractQueuedSynchronizer通过构造一个基于阻塞的CLH队列容纳所有的阻塞线程,而对该队列的操作均通过Lock-Free(CAS)操作,但对已经获得锁的线程而言,ReentrantLock实现了偏向锁的功能。

    synchronized 的底层也是一个基于CAS操作的等待队列,但JVM实现的更精细,把等待队列分为ContentionList和EntryList,目的是为了降低线程的出列速度;当然也实现了偏向锁,从数据结构来说二者设计没有本质区别。但synchronized还实现了自旋锁,并针对不同的系统和硬件体系进行了优 化,而Lock则完全依靠系统阻塞挂起等待线程。

    当然Lock比synchronized更适合在应用层扩展,可以继承 AbstractQueuedSynchronizer定义各种实现,比如实现读写锁(ReadWriteLock),公平或不公平锁;同时,Lock对 应的Condition也比wait/notify要方便的多、灵活的多。

     

    state值,若为0,意味着此时没有线程获取到资源

    简述总结:

        总体来讲线程获取锁要经历以下过程(非公平):

        1、调用lock方法,会先进行cas操作看下可否设置同步状态1成功,如果成功执行临界区代码

        2、如果不成功获取同步状态,如果状态是0那么cas设置为1.

        3、如果同步状态既不是0也不是自身线程持有会把当前线程构造成一个节点。

        4、把当前线程节点CAS的方式放入队列中,行为上线程阻塞,内部自旋获取状态。

        (acquireQueued的主要作用是把已经追加到队列的线程节点进行阻塞,但阻塞前又通过tryAccquire重试是否能获得锁,如果重试成功能则无需阻塞,直接返回。)

        5、线程释放锁,唤醒队列第一个节点,参与竞争。重复上述。

     


    synchronized和lock的底层区别*

    synchronized的底层也是一个基于CAS操作的等待队列,但JVM实现的更精细,把等待队列分为ContentionList和EntryList,目的是为了降低线程的出列速度;当然也实现了偏向锁,从数据结构来说二者设计没有本质区别。但synchronized还实现了自旋锁,并针对不同的系统和硬件体系进行了优化,而Lock则完全依靠系统阻塞挂起等待线程。**

     

    当然Lock比synchronized更适合在应用层扩展,可以继承AbstractQueuedSynchronizer定义各种实现,比如实现读写锁(ReadWriteLock),公平或不公平锁;同时,Lock对应的Condition也比wait/notify要方便的多、灵活的多。

    ReentrantLock是一个可重入的互斥锁,ReentrantLock由最近成功获取锁,还没有释放的线程所拥有

    ReentrantLock与synchronized的区别

    --ReentrantLock的lock机制有2种,忽略中断锁和响应中断锁

     

    --synchronized实现的锁机制是可重入的,主要区别是中断控制和竞争锁公平策略


    两者区别:

    1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;

    2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;

    3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;

    4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;

    5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)

    6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

     

    synchronized底层实现

    synchronized 属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层操作系统的 Mutex Lock 来实现的,而操作系统实现线程之间的切换时需要从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的 synchronized 效率低的原因。庆幸的是在 Java 6 之后 Java 官方从 JVM 层面对 synchronized 进行了较大优化,所以现在的 synchronized 锁效率也优化得很不错了。Java 6 之后,为了减少获得锁和释放锁所带来的性能消耗,引入了轻量级锁和偏向锁,

     

    Lock底层实现

    Lock底层实现基于AQS实现,采用线程独占的方式,在硬件层面依赖特殊的CPU指令(CAS)。

    简单来说,ReenTrantLock的实现是一种自旋锁,通过循环调用CAS操作来实现加锁。它的性能比较好也是因为避免了使线程进入内核态的阻塞状态。想尽办法避免线程进入内核的阻塞状态是我们去分析和理解锁设计的关键钥匙。

     

    volatile底层实现

    在JVM底层volatile是采用“内存屏障”来实现的。

     

    lock和Monitor的区别

    一、lock的底层本身是Monitor来实现的,所以Monitor可以实现lock的所有功能。

    二、Monitor有TryEnter的功能,可以防止出现死锁的问题,lock没有。

     

    展开全文
  • Lock锁的使用

    千次阅读 2018-05-07 20:36:53
    在Java多线程中,可以使用synchronized关键字实现线程之间的同步互斥,在jdk1.5后新增的...观察ReentrantLock类可以发现其实现了Lock接口public class ReentrantLock implements Lock,java.io.Serializable1、使用Re...

       在Java多线程中,可以使用synchronized关键字实现线程之间的同步互斥,在jdk1.5后新增的ReentrantLock类同样可达到此效果,且在使用上比synchronized更加灵活。

    观察ReentrantLock类可以发现其实现了Lock接口

    public class ReentrantLock implements Lock,java.io.Serializable

    1、使用ReentrantLock实现同步

       lock()方法:上锁

       unlock()方法:释放锁

    /*
     * 使用ReentrantLock类实现同步
     * */
    class MyReenrantLock implements Runnable{
    	//向上转型
    	private Lock lock = new ReentrantLock();
    	public void run() {
    		//上锁
    		lock.lock();
    		for(int i = 0; i < 5; i++) {
    			System.out.println("当前线程名: "+ Thread.currentThread().getName()+" ,i = "+i);
    		}
    		//释放锁
    		lock.unlock();
    	}
    }
    public class MyLock {
    	public static void main(String[] args) {
    		MyReenrantLock myReenrantLock =  new MyReenrantLock();
    		Thread thread1 = new Thread(myReenrantLock);
    		Thread thread2 = new Thread(myReenrantLock);
    		Thread thread3 = new Thread(myReenrantLock);
    		thread1.start();
    		thread2.start();
    		thread3.start();
    	}
    }

                           

    由此我们可以看出,只有当当前线程打印完毕后,其他的线程才可继续打印,线程打印的数据是分组打印,因为当前线程持有锁,但线程之间的打印顺序是随机的。

    即调用lock.lock()代码的线程就持有了“对象监视器”,其他线程只有等待锁被释放再次争抢。

    2、使用Condition实现等待/通知

    synchronized关键字结合wait()notify()notifyAll()方法的使用可以实现线程的等待与通知模式。在使用notify()notifyAll()方法进行通知时,被通知的线程是JVM随机选择的。

    ReentrantLock类同样可以实现该功能,需要借助Condition对象,可实现“选择性通知”。Condition类是jdk1.5提供的,且在一个Lock对象中可以创建多个Condition(对象监视器)实例。

    Condition类的await():是当前执行任务的线程处于等待状态

    /*
     * 错误的使用Condition实现等待、通知
     * */
    class MyCondition implements Runnable{
    	private Lock lock = new ReentrantLock();
    	public Condition condition = lock.newCondition();
    	public void run() {
    		try {
    			System.out.println("当前线程名:"+Thread.currentThread().getName()+" 开始等待时间:"+System.currentTimeMillis());
    			//线程等待
    			condition.await();
    			System.out.println("我陷入了等待...");
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }
    public class MyLock{
    	public static void main(String[] args) {
    		MyCondition myCondition = new MyCondition();
    		Thread thread1 = new Thread(myCondition,"线程1");
    		thread1.start();
    	}
    }

    观察运行结果可以发现,报出监视器出错的异常,解决的办法是我们必须在condition.await()方法调用前用lock.lock()代码获得同步监视器。对上述代码做出如下修改:

    /*
     * 使用Condition实现等待
     * */
    class MyCondition implements Runnable{
    	private Lock lock = new ReentrantLock();
    	public Condition condition = lock.newCondition();
    	public void run() {
    		try {
    			//上锁
    			lock.lock();
    			System.out.println("当前线程名:"+Thread.currentThread().getName()+" 开始等待时间:"+System.currentTimeMillis());
    			//线程等待
    			condition.await();
    			System.out.println("我陷入了等待...");
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}finally {
    			//释放锁
    			lock.unlock();
    			System.out.println("锁释放了!");
    		}
    	}
    }
    public class MyLock{
    	public static void main(String[] args) {
    		MyCondition myCondition = new MyCondition();
    		Thread thread1 = new Thread(myCondition,"线程1");
    		thread1.start();
    	}
    }

          

    在控制台只打印出一句,原因是调用了Condition对象的await()方法,是的当前执行任务的线程进入等待状态。

    Condition类的signal():是当前执行任务的线程处于等待状态

    /*
     * 使用Condition实现等待、通知
     * */
    class MyCondition implements Runnable{
    	private Lock lock = new ReentrantLock();
    	public Condition condition = lock.newCondition();
    	public void run() {
    		try {
    			//上锁
    			lock.lock();
    			System.out.println(" 开始等待时间:"+System.currentTimeMillis());
    			System.out.println("我陷入了等待...");
    			//线程等待
    			condition.await();
    			//释放锁
    			lock.unlock();
    			System.out.println("锁释放了!");
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    	//通知方法
    	public void signal(){
    		try {
    			lock.lock();
    			System.out.println("结束等待时间:"+System.currentTimeMillis());
    	     	//通知等待线程
    			condition.signal();
    		} finally {
    			lock.unlock();
    		}
    	}
    }
    public class MyLock{
    	public static void main(String[] args) throws InterruptedException {
    		MyCondition myCondition = new MyCondition();
    		Thread thread1 = new Thread(myCondition,"线程1");
    		thread1.start();
    		Thread.sleep(3000);
    		myCondition.signal();
    	}
    }
    

                

    观察结果我们成功地实现了等待通知。

    可以得知:Object类中的wait()方法等同于Condition类中的await()方法。

                    Object类中的wait(long timeout)方法等同于Condition类中的await(long time,TimeUnit unit)方法。

                    Object类中的notify()方法等同于Condition类中的singal()方法。

                   Object类中的notifyAll()方法等同于Condition类中的singalAll()方法。

       

    3、生产者消费者模式

    /*
     * 生产者、消费者模式
     * 一对一交替打印
     * */
    class MyServer{
    	private ReentrantLock lock = new ReentrantLock();
    	public Condition condition = lock.newCondition();
    	public Boolean flag = false;
    	public void set() {
    		try {
    			lock.lock();
    			while(flag == true) {
    				condition.await();
    			}
    			System.out.println("当前线程名:"+Thread.currentThread().getName()+" hello");
    			flag = true;
    			condition.signal();
    			} catch (InterruptedException e) {
    			e.printStackTrace();
    		}finally {
    			lock.unlock();
    		}
    	}
    	public void get() {
    		try {
    			lock.lock();
    			while(flag == false) {
    				condition.await();
    			}
    			System.out.println("当前线程名:"+Thread.currentThread().getName()+" lemon");
    			flag = false;
    			condition.signal();
    			} catch (InterruptedException e) {
    			e.printStackTrace();
    		}finally {
    			lock.unlock();
    		}
    	}
    }
    class MyCondition1 extends Thread{
    	private MyServer myServer;
    	public MyCondition1(MyServer myServer) {
    		super();
    		this.myServer = myServer;
    	}
    	public void run() {
    		for(int i = 0 ;i < Integer.MAX_VALUE;i++) {
    			myServer.set();
    		}
    	}	
    }
    class MyCondition2 extends Thread{
    	private MyServer myServer;
    	public MyCondition2(MyServer myServer) {
    		super();
    		this.myServer = myServer;
    	}
    	public void run() {
    		for(int i = 0 ;i < Integer.MAX_VALUE;i++) {
    			myServer.get();
    		}
    	}	
    }
    public class MyLock{
    	public static void main(String[] args) throws InterruptedException {
    		MyServer myServer = new MyServer();
    		MyCondition1  myCondition1 = new MyCondition1(myServer);
    		MyCondition2  myCondition2 = new MyCondition2(myServer);
    		myCondition1.start();
    		myCondition2.start();
    	}
    }

                      

    /*
     * 生产者、消费者模式
     * 多对多交替打印
     * */
    class MyServer{
    	private ReentrantLock lock = new ReentrantLock();
    	public Condition condition = lock.newCondition();
    	public Boolean flag = false;
    	public void set() {
    		try {
    			lock.lock();
    			while(flag == true) {
    				System.out.println("可能会有连续的hello进行打印");
    				condition.await();
    			}
    			System.out.println("当前线程名:"+Thread.currentThread().getName()+" hello");
    			flag = true;
    			condition.signal();
    			} catch (InterruptedException e) {
    			e.printStackTrace();
    		}finally {
    			lock.unlock();
    		}
    	}
    	public void get() {
    		try {
    			lock.lock();
    			while(flag == false) {
    				System.out.println("可能会有连续的lemon进行打印");
    				condition.await();
    			}
    			System.out.println("当前线程名:"+Thread.currentThread().getName()+" lemon");
    			flag = false;
    			condition.signal();
    			} catch (InterruptedException e) {
    			e.printStackTrace();
    		}finally {
    			lock.unlock();
    		}
    	}
    }
    class MyCondition1 extends Thread{
    	private MyServer myServer;
    	public MyCondition1(MyServer myServer) {
    		super();
    		this.myServer = myServer;
    	}
    	public void run() {
    		for(int i = 0 ;i < Integer.MAX_VALUE;i++) {
    			myServer.set();
    		}
    	}	
    }
    class MyCondition2 extends Thread{
    	private MyServer myServer;
    	public MyCondition2(MyServer myServer) {
    		super();
    		this.myServer = myServer;
    	}
    	public void run() {
    		for(int i = 0 ;i < Integer.MAX_VALUE;i++) {
    			myServer.get();
    		}
    	}	
    }
    public class MyLock{
    	public static void main(String[] args) throws InterruptedException {
    		MyServer myServer = new MyServer();
    		MyCondition1[]  myCondition1 = new MyCondition1[10];
    		MyCondition2[]  myCondition2 = new MyCondition2[10];
    		for(int i = 0; i < 10; i++) {
    			myCondition1[i] = new MyCondition1(myServer);
    			myCondition2[i] = new MyCondition2(myServer);
    			myCondition1[i].start();
    			myCondition2[i].start();
    		}
    	}
    }

                            

    4、公平锁与非公平锁

    Lock分为“公平锁”和“非公平锁”。

    公平锁:表示线程获取锁的顺序是按照线程加锁的顺序来的进行分配的,即先来先得FIFO先进先出顺序。

    非公平锁:一种获取锁的抢占机制,是随机拿到锁的,和公平锁不一样的是先来的不一定先拿到锁,这个方式可能造成某些线程一直拿不到锁,结果就是不公平的·。

    /*
     * 公平锁
     * */
    class MyService{
    	private ReentrantLock lock;
    	public MyService(boolean isFair) {
    		super();
    		lock = new ReentrantLock(isFair);
    	}
    	public void serviceMethod() {
    		try {
    			lock.lock();
    			System.out.println("线程名:"+Thread.currentThread().getName()+"获得锁定");
    		} finally {
    			lock.unlock();
    		}
    	}
    }
    public class MyLock{
    	public static void main(String[] args) {
    		//设置当前为true公平锁
    		final MyService myService = new MyService(true);
    		Runnable runnable = new Runnable() {
    			public void run() {
    				System.out.println("线程名:"+Thread.currentThread().getName()+"运行了");
    				myService.serviceMethod();
    			}
    		};
    		Thread[] threads = new Thread[10];
    		for(int i = 0;i < 10; i++) {
    			threads[i] = new Thread(runnable);
    		}
    		for(int i = 0;i < 10; i++) {
    			threads[i].start();
    		}
    	}
    }
               

    由打印结果可以看出,基本呈现有序的状态,这就是公平锁的特点。

    /*
     * 非公平锁
     * */
    class MyService{
    	private ReentrantLock lock;
    	public MyService(boolean isFair) {
    		super();
    		lock = new ReentrantLock(isFair);
    	}
    	public void serviceMethod() {
    		try {
    			lock.lock();
    			System.out.println("线程名:"+Thread.currentThread().getName()+"获得锁定");
    		} finally {
    			lock.unlock();
    		}
    	}
    }
    public class MyLock{
    	public static void main(String[] args) {
    		//设置当前为true公平锁
    		final MyService myService = new MyService(false);
    		Runnable runnable = new Runnable() {
    			public void run() {
    				System.out.println("线程名:"+Thread.currentThread().getName()+"运行了");
    				myService.serviceMethod();
    			}
    		};
    		Thread[] threads = new Thread[10];
    		for(int i = 0;i < 10; i++) {
    			threads[i] = new Thread(runnable);
    		}
    		for(int i = 0;i < 10; i++) {
    			threads[i].start();
    		}
    	}
    }


    非公平锁的运行结果基本都是无须的,则可以表明先start()启动的线程并不一定先获得锁。

    5、使用ReentrantReadWriteLock

    ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后的任务。这样虽然保证了实例变量的线程安全性,但是效率低下。所以在Java中提供有读写锁ReentrantReadWriteLock类,使其效率可以加快。在某些不需要操作实例变量的方法中,完全可以使用ReentrantReadWriteLock来提升该方法代码运行速度。

    读写锁表示两个锁:

    读操作相关的锁,也成为共享锁。

    写操作相关的锁,也叫排他锁。

    多个读锁之间不互斥,读锁与写锁互斥,多个写锁互斥。

    在没有线程Thread进行写入操作时,进行读操作的多个Thread可以获取读锁,但是进行写入操作时的Thread只有获取写锁后才能进行写入操作。

    1)多个读锁共享

    /*
     * 多个读锁共享
     * */
    class MyService{
    	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    	public void read() {
    		try {
    			//读锁
    			lock.readLock().lock();
    			System.out.println("线程名: "+Thread.currentThread().getName()+"获取读锁" );
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}finally {
    			//释放读锁
    			lock.readLock().unlock();
    		}
    	}
    }
    //线程1
    class Thread1 extends Thread{
    	private MyService myService;
    	public Thread1(MyService myService) {
    		super();
    		this.myService = myService;
    	}
    	public void run() {
    		myService.read();
    	}
    }
    //线程2
    class Thread2 extends Thread{
    	private MyService myService;
    	public Thread2(MyService myService) {
    		super();
    		this.myService = myService;
    	}
    	public void run() {
    		myService.read();
    	}
    }
    public class MyLock{
    	public static void main(String[] args) {
    		MyService myService = new MyService();
    		Thread1 thread1 = new Thread1(myService);
    		Thread2 thread2 = new Thread2(myService);
    		thread1.start();
    		thread2.start();
    		
    	}
    }

                    

    从打印结果可以看出,两个线程几乎同时进入lock()方法后面的代码。

    说明在此时使用lock.readLock()读锁可以提高程序运行效率,允许多个线程同时执行lock()方法后的代码。

    2)多个写锁互斥

    /*
     * 多个写锁互斥
     * */
    class MyService{
    	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    	public void write() {
    		try {
    			//写锁
    			lock.writeLock().lock();
    			System.out.println("线程名: "+Thread.currentThread().getName()+"获取写锁,获得时间:"+System.currentTimeMillis() );
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}finally {
    			//释放写锁
    			lock.writeLock().unlock();
    		}
    	}
    }
    //线程1
    class Thread1 extends Thread{
    	private MyService myService;
    	public Thread1(MyService myService) {
    		super();
    		this.myService = myService;
    	}
    	public void run() {
    		myService.write();
    	}
    }
    //线程2
    class Thread2 extends Thread{
    	private MyService myService;
    	public Thread2(MyService myService) {
    		super();
    		this.myService = myService;
    	}
    	public void run() {
    		myService.write();
    	}
    }
    public class MyLock{
    	public static void main(String[] args) {
    		MyService myService = new MyService();
    		Thread1 thread1 = new Thread1(myService);
    		Thread2 thread2 = new Thread2(myService);
    		thread1.start();
    		thread2.start();
    		
    	}
    }

                       

    使用写锁代码writeLock.lock()的效果就是同一时间只允许一个线程执行lock()方法后的代码。

    (3)读写/写读互斥

    /*
     * 读写/写读互斥,
     * */
    class MyService{
    	private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    	public void read() {
    		try {
    			//读锁
    			lock.readLock().lock();
    			System.out.println("线程名: "+Thread.currentThread().getName()+"获取读锁,获得时间:"+System.currentTimeMillis() );
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}finally {
    			//释放读锁
    			lock.readLock().unlock();
    		}
    	}
    	public void write() {
    		try {
    			//写锁
    			lock.writeLock().lock();
    			System.out.println("线程名: "+Thread.currentThread().getName()+"获取写锁,获得时间:"+System.currentTimeMillis() );
    			Thread.sleep(1000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}finally {
    			//释放写锁
    			lock.writeLock().unlock();
    		}
    	}
    }
    //线程1
    class Thread1 extends Thread{
    	private MyService myService;
    	public Thread1(MyService myService) {
    		super();
    		this.myService = myService;
    	}
    	public void run() {
    		myService.read();
    	}
    }
    //线程2
    class Thread2 extends Thread{
    	private MyService myService;
    	public Thread2(MyService myService) {
    		super();
    		this.myService = myService;
    	}
    	public void run() {
    		myService.write();
    	}
    }
    public class MyLock{
    	public static void main(String[] args) {
    		MyService myService = new MyService();
    		Thread1 thread1 = new Thread1(myService);
    		Thread2 thread2 = new Thread2(myService);
    		thread1.start();
    		thread2.start();	
    	}
    }

                     

    此运行结果说明“读写/写读”操作是互斥的。

    由此可表明:只要出现“写”操作,就是互斥的。


     



    展开全文
  • LOCK锁

    2018-12-07 14:24:35
    LOCK锁 1. synchronized的缺陷 synchronized是java中的一个关键字,也就是说是Java语言内置的特性。那么为什么会出现Lock呢? 如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时...
  • Lock锁

    2018-06-01 17:13:57
    我们在介绍Lock锁之前先介绍锁的几个概念。1. 可重入锁如synchronized和ReentrantLock都是可重入锁,可重入性实际上表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。当一个线程执行到某个...
  • Lock锁简介 1.用处: Lock锁与synchronized一样,都是可以用来控制同步访问的。 2.有了synchronized,为什么还要Lock锁呢? 那就要谈到synchronized的缺点,主要是三个方面。 A 有时候用synchronized修饰的...
  • Lock锁的简单使用

    千次阅读 2018-08-24 16:29:24
    Lock锁与synchronize关键字的区别:  1.Lock锁可以在我们需要的地方显式的调用,或者中断,以及超时获取锁等更加灵活的锁操作;但是失去了synchronize隐式获取与释放的便捷性.  2.Lock锁必须使用unLock释放,因此我们...
  • [java]lock锁介绍

    千次阅读 2018-12-26 22:15:16
    的相关概念介绍  若有不正之处请多多谅解,并欢迎批评指正。  请尊重作者劳动成果,转载请标明原文链接: &amp;nbsp; http://www.cnblogs.com/dolphin0520/p/3923167.html 一.synchroniz...
  • lock锁的底层实现

    千次阅读 2018-04-15 18:02:42
    上一篇博文中我大致讲解了一下lock锁与sychronized的区别及它们各自的应用优劣,那么是什么造成它们各自应用上的偏向呢,这当然取决于它们的底层实现,所以今天我们就先来看一下lock锁的底层原理吧,因为lock锁的...
  • Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我们拿Java线程(二)中的一个例子简单的实现一下和...
  • 详解synchronized与Lock的区别与使用

    万次阅读 多人点赞 2017-03-22 13:12:02
    昨天在学习别人分享的面试经验时,看到Lock的使用。想起自己在上次面试也遇到了synchronized与Lock的区别与使用。于是,我整理了两者的区别和使用情况,同时,对synchronized的使用过程一些常见问题的总结,最后是...
  • Java---Lock锁(AQS与Lock区别)

    千次阅读 2018-11-21 08:49:38
    什么是Lock锁? 在Java中锁是出现是为了防止多个线程访问同一资源,锁有内建锁(synchronized),synchronized关键字实现锁是隐式加解锁,在JDK5后,java.util.concurrent包中增加了lock接口,它提供了和...
  • Lock锁-------tryLock()方法

    千次阅读 2018-10-04 17:32:48
    这一次主要学习Lock接口中的**tryLock()**方法。 tryLock()方法是有返回值的,返回值是Boolean类型。它表示的是用来尝试获取:成功获取则返回true;获取失败则返回false,这个方法无论如何都会立即返回。不会像...
  • 本文详细讲述了显示Lock锁的各项机制:可中断锁、公平锁、读锁、写锁、条件变量实现线程间协作、await、signal、signalAll。 Java 5中引入了新的锁机制——java.util.concurrent.locks中的显式的互斥锁:Lock接口,...
  • java中lock锁使用以及AQS理解

    千次阅读 2018-11-26 15:51:41
    lock锁 和关键字synchronized内建锁不同,Lock锁是完全由java语言实现的,Lock锁的代码在Java.util包下来完成我们同步访问临界资源。synchronized锁会使得其他线程阻塞等待等待被唤醒,而Lock使得其他请求访问的...
  • Lock锁和Condition条件

    千次阅读 2018-03-17 19:21:34
    浅谈Synchronized: synchronized是Java的一个关键字,也就是Java语言内置的特性,如果一个代码块被synchronized修饰了,当一个线程获取了对应的,执行代码块时,其他线程便只能一直等待,等待获取的线程释放,而获取...
  • 多线程 之 Lock 的实现原理

    千次阅读 2017-04-05 16:44:52
    1. Lock 的简介及使用 Lock完全用Java写成,在java这个层面是无关JVM实现的,Lock 接口主要有一下实现 //尝试获取,获取成功则返回,否则阻塞当前线程 void lock(); //尝试获取,线程在成功...
  • Lock锁以及死锁的形成与解决

    千次阅读 2018-05-31 16:51:54
    上面学到的synchronized可以帮助我们在多线程环境...此实现允许更灵活的结构可以使用Lock锁进行具体的锁定操作类 提供了具体的实现类:ReentrantLockLock锁中的方法:加锁(lock())并且去释放锁(unlock())例...
  • 当一个线程拿到Lock锁之后,其锁定的是代码块,或是方法,或是对象 吗
  • 多线程之Lock锁

    2020-06-07 21:27:38
    Lock锁 从JDK5.0开始,Java提供了更强大的线程同步机制,通过显示定义同步锁对象来实现同步。同步锁使用Lock对象充当。 java.util.concurrent.locks.Lock 接口时控制多个线程对共享资源进行访问的工具。锁提供了对...
  • lock锁和Synchronize的区别

    千次阅读 2019-09-17 15:24:16
    Java为此也提供了2种机制,synchronized和lock。做为Java爱好者,自然少不了对比一下这2种机制,也能从中学到些分布式开发需要注意的地方。 我们先从最简单的入手,逐步分析这2种的区别。 一、synchronized和lock...
  • Lock) 从JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步对象来实现同步。同步使用Lock对象充当 java.util.locks.Lock接口是控制多个线程对共享资源进行访问的工具。提供了对共享...
  • Synchronized锁和Lock锁 1、synchronized是JVM层面实现的,java提供的关键字,Lock是API层面的锁。 2、synchronized不需要手动释放锁,底层会自动释放,Lock则需要手动释放锁,否则有可能导致死锁。 3、synchronized...
  • wakelock锁

    千次阅读 2015-03-14 23:20:53
    linaro 发布的内核版本中,提供了一套wakelock接口与google的wakelock接口实现了无缝对接,该套接口共有6个; struct wake_lock{ struct wakeup_source ws; }; static inline void wake_lock_init(struct wake_lock...
  • synchronized关键字与Lock锁机制的区别
  • public interface Lock { class TimeOutException extends Exception { public TimeOutException(String message) { super(message); } } void lock() throws InterruptedException; void lock(...
  • thinkphp lock 的使用和例子

    千次阅读 2019-11-01 09:51:24
    用innoDB的事务和就能解决这个问题。在我们当前行更新还没结束的时候,select查询此行的数据会被起来。 比如我们数据库有这样两行数据 我们把id=1的num数据更新为1000,sleep10秒,这时候我们select id=1的...
  • 关于Lock锁,我们简单说一下Lock锁的包是java.util.concurrent.locks.*;/* jdk1.5多线程的实现方式: jdk1.5之前对锁的操作是隐式的 synchronized(对象)//获取锁 {}//释放锁jdk1.5对锁的操作是显式的: 有一个描述锁...
  • C# 线程Lock锁 (只能锁定对象)

    千次阅读 2018-07-09 14:50:36
    为什么要locklock了什么?当我们使用线程的时候,效率最高的方式当然是异步,即各个线程同时运行,其间不相互依赖和等待。但当不同的线程都需要访问某个资源的时候,就需要同步机制了,也就是说当对同一个资源...

空空如也

1 2 3 4 5 ... 20
收藏数 794,021
精华内容 317,608
关键字:

lock锁