Java多线程学习篇Lock

Posted 黑.白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程学习篇Lock相关的知识,希望对你有一定的参考价值。

Lock 是Java多线程的一个同步机制,用来控制线程对共享资源的访问。线程在执行同步方法或者代码块之前必须先获得一个锁。

Lock 的 lock() 和 unlock() 方法;

  lock():获得一个锁,如果锁不可用,则当前线程将因线程调度目的而被禁用,并在获得锁之前处于休眠状态。

  unlock():释放掉获得的锁。

Lock的作用范围:

  1. 若 Lock 是静态的,则作用范围是整个类。

    技术分享图片
    public class Test {
        public static void main(String[] args) {
            Thread thread_one = new Thread(new Account(), "Thread_ONE");
            Thread thread_two = new Thread(new Account(), "Thread_Tow");
            thread_one.start();
            thread_two.start();
        }
    }
    class Account implements Runnable{
        static Lock lock = new ReentrantLock();  // 静态的Lock
        static int Count = 0;
        @Override
        public void run(){
            runTest();
        }
        public void runTest() {
            lock.lock();
            try{
                for(int i = 0; i < 5; ++i)
                {
                    Count++;
                    System.out.println(Thread.currentThread().getName() + " " + Count);
                }
            }
            finally {
                lock.unlock();
            }
        }
    }
    //输出:
    //Thread_ONE 1
    //Thread_ONE 2
    //Thread_ONE 3
    //Thread_ONE 4
    //Thread_ONE 5
    //Thread_Tow 6
    //Thread_Tow 7
    //Thread_Tow 8
    //Thread_Tow 9
    //Thread_Tow 10
    静态的Lock
  2. 若 Lock 是非静态的,则范围是整个对象。

    技术分享图片
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Test1 {
        public static void main(String[] args) {
            Thread thread_one = new Thread(new Account(), "Thread_ONE");
            Thread thread_two = new Thread(new Account(), "Thread_Tow");
            thread_one.start();
            thread_two.start();
        }
    }
    class Account implements Runnable{
        Lock lock = new ReentrantLock();  // 非静态的Lock
        static int Count = 0;
        @Override
        public void run(){
            runTest();
        }
        public void runTest() {
            lock.lock();
            try{
                for(int i = 0; i < 5; ++i)
                {
                    Count++;
                    System.out.println(Thread.currentThread().getName() + " " + Count);
                }
            }
            finally {
                lock.unlock();
            }
        }
    }
    //输出:
    //Thread_Tow 2
    //Thread_ONE 2
    //Thread_Tow 3
    //Thread_ONE 4
    //Thread_Tow 5
    //Thread_ONE 6
    //Thread_Tow 7
    //Thread_ONE 8
    //Thread_Tow 9
    //Thread_ONE 10
    非静态的Lock

Lock的一个简单的实现

public class Lock{
    
    private boolean isLocked = false;

    public synchronized void lock()throws InterruptedException{
        while(isLocked){ // 使用whlie而不是if
            wait(); // 终止线程并且释放对象的锁,当线程被通知后重新启动,锁就会自动被重新获取
        }
        isLocked = true;
    }
    public synchronized void unlock(){
        isLocked = false;
        notify();//通知一个等待的线程重新获取锁并且恢复执行
    }
}

这里的方法lock(),若当前的锁被其它线程获取而锁住,这调用wait()方法,让线程等待,直到其他线程调用unlock()中的notify()方法而重新启动。这里用的是while而不是if,防止wait()退出后锁还是被其它线程锁住,(比如锁又被其它线程抢走),(lock() 和 notify()、notifyAll() 是Object对象中的方法)


ReentrantLock 是 Lock接口 的一种实现。
public class ReentrantLock implements Lock, java.io.Serializable{ }

 

ReentrantLock 具有可重入性。
什么是可可重入性呢?先举个例子。
public class Reentrant2{

    Lock lock = new Lock(); // 这里的Lock用的是上面写的 public class Lock { }

    public outer(){
        lock.lock();
        inner();
        lock.unlock();
    }

    public synchronized inner(){
        lock.lock();
        //do something
        lock.unlock();
    }
}
假设某个线程调用了outer()方法,这时,该线程调用了lock(),获取了锁,然后再调用inner(),但是inner()内再次调用 lock() 获取锁,可是这锁已经被第一次调用而锁上,所以会进入while调用wait()从而导致死锁。
可重入性可以让线程调用自身持有的锁(如果该锁是可重入的)。

改写一下Lock的代码
public class Lock{

    boolean isLocked = false;
    Thread  lockedBy = null;
    int     lockedCount = 0;

    public synchronized void lock()
            throws InterruptedException{
        Thread callingThread = Thread.currentThread();
        while(isLocked && lockedBy != callingThread){  // 若锁被不是自己持有的锁锁住
            wait();
        }
        isLocked = true;
        lockedCount++;
        lockedBy = callingThread;
    }
    
    public synchronized void unlock(){
        if(Thread.curentThread() == this.lockedBy){
            lockedCount--;

            if(lockedCount == 0){
                isLocked = false;
                notify();
            }
        }
    }
  ...
}
while 循环中加了一个判断 lockedBy != callingThread,这样就不会被自己的锁锁住,并且用 lockedCount 计数一下,只有当 lockedCount 为 0 时,才会调用notify()方法。

最后说一点,使用锁的时候最好使用try,以防一些想不到的问题导致锁没有释放。
lock.lock();
try{
  //do critical section code, which may throw exception
} finally {
  lock.unlock();
}

 

参考:http://tutorials.jenkov.com/java-concurrency/locks.html
































































以上是关于Java多线程学习篇Lock的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程与并发库高级应用-工具类介绍

java学习第22天(关于java中的锁LOCK及多线程的面试题)

Java---JUC并发篇(多线程详细版)

Java---JUC并发篇(多线程详细版)

Java多线程与并发库高级应用-工具类介绍

“全栈2019”Java多线程第二十七章:Lock获取lock/释放unlock锁