Java并发学习笔记6 Lock锁

bilibili-Java并发学习笔记6 Lock锁

基于 java 1.8.0

P24_Lock锁机制深入详解

/**
 * Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
 * 锁是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。不过,某些锁可能允许对共享资源并发访问,如 ReadWriteLock 的读取锁。
 * synchronized 方法或语句的使用提供了对与每个对象相关的隐式监视器锁的访问,但却强制所有锁获取和释放均要出现在一个块结构中:当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的词法范围内释放所有锁。
 * 虽然 synchronized 方法和语句的范围机制使得使用监视器锁编程方便了很多,而且还帮助避免了很多涉及到锁的常见编程错误,但有时也需要以更为灵活的方式使用锁。例如,某些遍历并发访问的数据结果的算法要求使用 "hand-over-hand" 或 "chain locking":获取节点 A 的锁,然后再获取节点 B 的锁,然后释放 A 并获取 C,然后释放 B 并获取 D,依此类推。Lock 接口的实现允许锁在不同的作用范围内获取和释放,并允许以任何顺序获取和释放多个锁,从而支持使用这种技术。
 * 随着灵活性的增加,也带来了更多的责任。不使用块结构锁就失去了使用 synchronized 方法和语句时会出现的锁自动释放功能。在大多数情况下,应该使用以下语句:
        Lock l = ...;
        l.lock();
        try {
            // access the resource protected by this lock
        } finally {
            l.unlock();
        }
 * 锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁。
 * Lock 实现提供了使用 synchronized 方法和语句所没有的其他功能,包括提供了一个非块结构的获取锁尝试 (tryLock())、一个获取可中断锁的尝试 (lockInterruptibly()) 和一个获取超时失效锁的尝试 (tryLock(long, TimeUnit))。
 * Lock 类还可以提供与隐式监视器锁完全不同的行为和语义,如保证排序、非重入用法或死锁检测。如果某个实现提供了这样特殊的语义,则该实现必须对这些语义加以记录。
 * 注意,Lock 实例只是普通的对象,其本身可以在 synchronized 语句中作为目标使用。获取 Lock 实例的监视器锁与调用该实例的任何 lock() 方法没有特别的关系。为了避免混淆,建议除了在其自身的实现中之外,决不要以这种方式使用 Lock 实例。
 * 除非另有说明,否则为任何参数传递 null 值都将导致抛出 NullPointerException。
 * ---
 * 内存同步
 * 所有 Lock 实现都必须 实施与内置监视器锁提供的相同内存同步语义,如 [The Java Language Specification, Third Edition (17.4 Memory Model)](https://docs.oracle.com/javase/specs/jls/se6/html/memory.html#17.4) 中所描述的:
 * - 成功的 lock 操作与成功的 Lock 操作具有同样的内存同步效应。
 * - 成功的 unlock 操作与成功的 Unlock 操作具有同样的内存同步效应。
 * 不成功的锁定与取消锁定操作以及重入锁定/取消锁定操作都不需要任何内存同步效果。
 * ---
 * 实现注意事项
 * 三种形式的锁获取(可中断、不可中断和定时)在其性能特征、排序保证或其他实现质量上可能会有所不同。而且,对于给定的 Lock 类,可能没有中断正在进行的 锁获取的能力。因此,并不要求实现为所有三种形式的锁获取定义相同的保证或语义,也不要求其支持中断正在进行的锁获取。实现必需清楚地对每个锁定方法所提供的语义和保证进行记录。还必须遵守此接口中定义的中断语义,以便为锁获取中断提供支持:完全支持中断,或仅在进入方法时支持中断。
 * 由于中断通常意味着取消,而通常又很少进行中断检查,因此,相对于普通方法返回而言,实现可能更喜欢响应某个中断。即使出现在另一个操作后的中断可能会释放线程锁时也是如此。实现应记录此行为。
 * 
 * @see ReentrantLock
 * @see Condition
 * @see ReadWriteLock
 *
 * @since 1.5
 * @author Doug Lea
 */
public interface Lock {

    /**
     * Acquires the lock.
     * 获取锁。
     *
     * 

If the lock is not available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until the * lock has been acquired. * 如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态。 * *

Implementation Considerations * 实现注意事项 * *

A {@code Lock} implementation may be able to detect erroneous use * of the lock, such as an invocation that would cause deadlock, and * may throw an (unchecked) exception in such circumstances. The * circumstances and the exception type must be documented by that * {@code Lock} implementation. * Lock 实现可能能够检测到锁的错误使用,比如会导致死锁的调用,在那种环境下还可能抛出一个 (unchecked) 异常。Lock 实现必须对环境和异常类型进行记录。 */ void lock(); /** * Acquires the lock unless the current thread is * {@linkplain Thread#interrupt interrupted}. 如果当前线程未被中断,则获取锁。 * *

Acquires the lock if it is available and returns immediately. 如果锁可用,则获取锁,并立即返回。 * *

If the lock is not available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of two things happens: 如果锁不可用,出于线程调度目的,将禁用当前线程,并且在发生以下两种情况之一以前,该线程将一直处于休眠状态: * *

    *
  • The lock is acquired by the current thread; or 锁由当前线程获得;或者 *
  • Some other thread {@linkplain Thread#interrupt interrupts} the * current thread, and interruption of lock acquisition is supported. 其他某个线程中断当前线程,并且支持对锁获取的中断。 *
* *

If the current thread: 如果当前线程 *

    *
  • has its interrupted status set on entry to this method; or 在进入此方法时已经设置了该线程的中断状态;或者 *
  • is {@linkplain Thread#interrupt interrupted} while acquiring the * lock, and interruption of lock acquisition is supported, 在获取锁时被中断,并且支持对锁获取的中断, *
* then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. 则将抛出 InterruptedException,并清除当前线程的已中断状态。 * *

Implementation Considerations 实现注意事项 * *

The ability to interrupt a lock acquisition in some * implementations may not be possible, and if possible may be an * expensive operation. The programmer should be aware that this * may be the case. An implementation should document when this is * the case. 在某些实现中可能无法中断锁获取,即使可能,该操作的开销也很大。程序员应该知道可能会发生这种情况。在这种情况下,该实现应该对此进行记录。 * *

An implementation can favor responding to an interrupt over * normal method return. 相对于普通方法返回而言,实现可能更喜欢响应某个中断。 * *

A {@code Lock} implementation may be able to detect * erroneous use of the lock, such as an invocation that would * cause deadlock, and may throw an (unchecked) exception in such * circumstances. The circumstances and the exception type must * be documented by that {@code Lock} implementation. Lock 实现可能可以检测锁的错误用法,例如,某个调用可能导致死锁,在特定的环境中可能抛出(未经检查的)异常。该 Lock 实现必须对环境和异常类型进行记录。 * * @throws InterruptedException if the current thread is * interrupted while acquiring the lock (and interruption * of lock acquisition is supported) 如果在获取锁时,当前线程被中断(并且支持对锁获取的中断)。 */ void lockInterruptibly() throws InterruptedException; /** * Acquires the lock only if it is free at the time of invocation. 仅在调用时锁为空闲状态才获取该锁。 * *

Acquires the lock if it is available and returns immediately * with the value {@code true}. * If the lock is not available then this method will return * immediately with the value {@code false}. 如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false。 * *

A typical usage idiom for this method would be: 此方法的典型使用语句如下: *

 {@code
     * Lock lock = ...;
     * if (lock.tryLock()) {
     *   try {
     *     // manipulate protected state
     *   } finally {
     *     lock.unlock();
     *   }
     * } else {
     *   // perform alternative actions
     * }}
* * This usage ensures that the lock is unlocked if it was acquired, and * doesn't try to unlock if the lock was not acquired. 此用法可确保如果获取了锁,则会释放锁,如果未获取锁,则不会试图将其释放。 * * @return {@code true} if the lock was acquired and * {@code false} otherwise 如果获取了锁,则返回 true;否则返回 false。 */
boolean tryLock(); /** * Acquires the lock if it is free within the given waiting time and the * current thread has not been {@linkplain Thread#interrupt interrupted}. 如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁。 * *

If the lock is available this method returns immediately * with the value {@code true}. * If the lock is not available then * the current thread becomes disabled for thread scheduling * purposes and lies dormant until one of three things happens: 如果锁可用,则此方法将立即返回值 true。如果锁不可用,出于线程调度目的,将禁用当前线程,并且在发生以下三种情况之一前,该线程将一直处于休眠状态: *

    *
  • The lock is acquired by the current thread; or 锁由当前线程获得;或者 *
  • Some other thread {@linkplain Thread#interrupt interrupts} the * current thread, and interruption of lock acquisition is supported; or 其他某个线程中断当前线程,并且支持对锁获取的中断;或者 *
  • The specified waiting time elapses 已超过指定的等待时间 *
* *

If the lock is acquired then the value {@code true} is returned. 如果获得了锁,则返回值 true。 * *

If the current thread: 如果当前线程: *

    *
  • has its interrupted status set on entry to this method; or 在进入此方法时已经设置了该线程的中断状态;或者 *
  • is {@linkplain Thread#interrupt interrupted} while acquiring * the lock, and interruption of lock acquisition is supported, 在获取锁时被中断,并且支持对锁获取的中断, *
* then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. 则将抛出 InterruptedException,并会清除当前线程的已中断状态。 * *

If the specified waiting time elapses then the value {@code false} * is returned. * If the time is * less than or equal to zero, the method will not wait at all. 如果超过了指定的等待时间,则将返回值 false。如果 time 小于等于 0,该方法将完全不等待。 * *

Implementation Considerations 实现注意事项 * *

The ability to interrupt a lock acquisition in some implementations * may not be possible, and if possible may * be an expensive operation. * The programmer should be aware that this may be the case. An * implementation should document when this is the case. 在某些实现中可能无法中断锁获取,即使可能,该操作的开销也很大。程序员应该知道可能会发生这种情况。在这种情况下,该实现应该对此进行记录。 * *

An implementation can favor responding to an interrupt over normal * method return, or reporting a timeout. 相对于普通方法返回而言,实现可能更喜欢响应某个中断,或者报告出现超时情况。 * *

A {@code Lock} implementation may be able to detect * erroneous use of the lock, such as an invocation that would cause * deadlock, and may throw an (unchecked) exception in such circumstances. * The circumstances and the exception type must be documented by that * {@code Lock} implementation. Lock 实现可能可以检测锁的错误用法,例如,某个调用可能导致死锁,在特定的环境中可能抛出(未经检查的)异常。该 Lock 实现必须对环境和异常类型进行记录。 * * @param time the maximum time to wait for the lock 等待锁的最长时间 * @param unit the time unit of the {@code time} argument 参数 time 的时间单位 * @return {@code true} if the lock was acquired and {@code false} * if the waiting time elapsed before the lock was acquired 如果获得了锁,则返回 true;如果在获取锁前超过了等待时间,则返回 false * * @throws InterruptedException if the current thread is interrupted * while acquiring the lock (and interruption of lock * acquisition is supported) 如果在获取锁时,当前线程被中断(并且支持对锁获取的中断) */ boolean tryLock(long time, TimeUnit unit) throws InterruptedException; /** * Releases the lock. 释放锁。 * *

Implementation Considerations * *

A {@code Lock} implementation will usually impose * restrictions on which thread can release a lock (typically only the * holder of the lock can release it) and may throw * an (unchecked) exception if the restriction is violated. * Any restrictions and the exception * type must be documented by that {@code Lock} implementation. * Lock 实现通常对哪个线程可以释放锁施加了限制(通常只有锁的保持者可以释放它),如果违背了这个限制,可能会抛出(未经检查的)异常。该 Lock 实现必须对所有限制和异常类型进行记录。 */ void unlock(); /** * Returns a new {@link Condition} instance that is bound to this * {@code Lock} instance. 返回绑定到此 Lock 实例的新 Condition 实例。 * *

Before waiting on the condition the lock must be held by the * current thread. * A call to {@link Condition#await()} will atomically release the lock * before waiting and re-acquire the lock before the wait returns. 在等待条件前,锁必须由当前线程保持。调用 Condition.await() 将在等待前以原子方式释放锁,并在等待返回前重新获取锁。 * *

Implementation Considerations * *

The exact operation of the {@link Condition} instance depends on * the {@code Lock} implementation and must be documented by that * implementation. Condition 实例的具体操作依赖于 Lock 实现,并且该实现必须对此加以记录。 * * @return A new {@link Condition} instance for this {@code Lock} instance 用于此 Lock 实例的新 Condition 实例 * @throws UnsupportedOperationException if this {@code Lock} * implementation does not support conditions 如果此 Lock 实现不支持条件 */ Condition newCondition(); }

P25_Lock锁方法原理详解


    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        lock.lock();
        try {
            // do something
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        if (lock.tryLock()) {
            try {
                // manipulate protected state
            } finally {
                lock.unlock();
            }
        } else {
            // perform alternative actions
        }
    }

    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        try {
            if (lock.tryLock(3L, TimeUnit.SECONDS)) {
                try {
                    // manipulate protected state
                } finally {
                    lock.unlock();
                }
            } else {
                // perform alternative actions
            }
        } catch (InterruptedException e) {
            // log
        }
    }

P26_Lock与synchronized关键字在底层的区别及实例分析

package new_package.thread.p24;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest2 {

    private Lock lock = new ReentrantLock();

    public void method1() {
        try {
            lock.lock();
            System.out.println("method1 invoke");
        } finally {
            // lock.unlock();
        }
    }

    public void method2() {
        try {
            lock.lock();
            System.out.println("method2 invoke");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        LockTest2 lockTest2 = new LockTest2();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                lockTest2.method1();

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                lockTest2.method2();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

package new_package.thread.p24;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest3 {

    private Lock lock = new ReentrantLock();

    public void method1(long time) {
        try {
            lock.lock();
            System.out.println("method1 invoke");
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            lock.unlock();
        }
    }

    public void method2(long time) {
        try {
            if (lock.tryLock(800, TimeUnit.MILLISECONDS)) {
                try {
                    System.out.println("method2 invoke");
                    try {
                        Thread.sleep(time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } finally {
                    lock.unlock();
                }
            } else {
                System.out.println("method2 not get lock");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        LockTest3 lockTest3 = new LockTest3();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                lockTest3.method1(2000);
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                lockTest3.method2(2000);
            }
        }).start();
    }
}

Lock 与 synchronized 的区别:

  • 获取方式
    • Lock 通过代码方式手动获取
    • synchronized 锁通过 JVM 获取
  • 实现方式
    • 通过 Java 代码方式实现
    • 通过 JVM 底层实现
  • 释放方式
    • 务必通过 unlock() 和 finally 块手工释放
    • JVM 自动释放
  • 具体类型
    • 提供了公平锁、非公平锁、可重入锁
    • 可重入锁

你可能感兴趣的:(Java多线程)