6.JUC之 Lock锁


目录
  • 6.1锁的可重入性
  • 6.2 ReentrantLock 可重入锁
  • 6.3 lockInterruptibly()方法
  • 6.4 tryLock
  • 6.5 newCondition()方法
  • 6.6 公平锁与非公平锁
  • 6.7 ReentrantLock中API
  • 6.8 ReentrantReadWriteLock 读写锁
    • 6.8.1 读读共享
    • 6.8.2 写写互斥
    • 6.8.3 读写互斥
  • 6.9 Lock与Synchronized区别

JUC代表java并发包的简写

在 jdk1.5 之后,并发包中新增了 Lock 接口(以及相关实现类)用来实现锁功能,Lock 接口提供了与 synchronized 关键字类似的同步功能,但需要在使用时手动获取锁和释放锁。

6.1锁的可重入性

锁的可重入是指,当一个线程获得一个对象锁后,再次请求该对象锁时仍然可以获得该对象的锁。

public class LockTest01 {

    private synchronized void print1() {
        System.out.println("同步方法1...");
        print2();
    }

    private synchronized void print2() {
        System.out.println("同步方法2...");
        print3();
    }

    private synchronized void print3() {
        System.out.println("同步方法3...");
    }

    public static void main(String[] args) {
        LockTest01 lockTest01 = new LockTest01();

        new Thread(new Runnable() {
            @Override
            public void run() {
                lockTest01.print1();
            }
        }).start();
    }
}
// 线程执行 print1()方法,默认 this 作为锁对象,在 print1()方法中调用了 print2()方法,注意当前线程还是持有 this 锁对象的
// print2()同步方法默认的锁对象也是 this 对象, 要执行 print2()必须先获得 this 锁对象,当前 this 对象被当前线程持有,可以 再次获得 this 锁对象, 这就是锁的可重入性. 假设锁不可重入的话,可能会造成死锁

6.2 ReentrantLock 可重入锁

调用 lock()方法获得锁,调用 unlock()释放锁,同样可以重入锁。

  Lock lock = new ReentrantLock();
  lock.lock();
  try{
	// 可能会出现线程安全的操作
  }finally{
	// 一定在finally中释放锁
	// 也不能把获取锁在try中进行,因为有可能在获取锁的时候抛出异常
    lock.unlock();
  }
public class C1Lock {
    // 总票数
    static int ticket = 10;

    public static void main(String[] args) {

        ReentrantLock lock = new ReentrantLock();

        Runnable runnable = () -> {
            // 循环买票
            while (true) {
                // 获得锁
                lock.lock();

                try {
                    Thread.sleep(100);
                    if (ticket > 0) {
                        ticket--;
                        System.out.println(Thread.currentThread().getName() 
                                           + "卖了一张票,剩余:" + ticket);
                    } else {
                        break;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            }
        };

        // 创建3个线程
        Thread t1 = new Thread(runnable, "窗口1");
        Thread t2 = new Thread(runnable, "窗口2");
        Thread t3 = new Thread(runnable, "窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

6.3 lockInterruptibly()方法

lockInterruptibly() 方法的作用:如果当前线程未被中断则获得锁,如果当前线程被中断则抛出异常。

对于 synchronized 内部锁来说,如果一个线程在等待锁,只有两个结果:要么该线程获得锁继续执行;要么就保持等待。
对于 ReentrantLock 可重入锁来说,提供另外一种可能,在等待锁的过程中,程序可以根据需要取消对锁的请求。

/**
 * 通过 ReentrantLock 锁的 lockInterruptibly()方法避免死锁的产生
 */
public class Test06 {
    static class IntLock implements Runnable {
        //创建两个 ReentrantLock 锁对象
        public static ReentrantLock lock1 = new ReentrantLock();
        public static ReentrantLock lock2 = new ReentrantLock();
        int lockNum; //定义整数变量,决定使用哪个锁

        public IntLock(int lockNum) {
            this.lockNum = lockNum;
        }

        @Override
        public void run() {
            try {
                if (lockNum % 2 == 1) {
                    //奇数,先锁 1,再锁 2
                    lock1.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获得锁 1,还需要获得锁 2");
                    Thread.sleep(new Random().nextInt(500));
                    lock2.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "同时获得了锁 1 与锁 2....");
                } else {
                    //偶数,先锁 2,再锁 1
                    lock2.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获得锁 2,还需要获得锁 1");
                    Thread.sleep(new Random().nextInt(500));
                    lock1.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "同时获得了锁1 与锁 2....");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (lock1.isHeldByCurrentThread()) { //判断当前线程是否持有该锁
                    lock1.unlock();
                }
                if (lock2.isHeldByCurrentThread()) {
                    lock2.unlock();
                }
                System.out.println(Thread.currentThread().getName() + "线程退出");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        IntLock intLock1 = new IntLock(11);
        IntLock intLock2 = new IntLock(22);
        Thread t1 = new Thread(intLock1);
        Thread t2 = new Thread(intLock2);
        t1.start();
        t2.start();
        //在 main 线程,等待 3000 秒,如果还有线程没有结束就中断该线程
        Thread.sleep(3000);
        //可以中断任何一个线程来解决死锁, t2 线程会放弃对锁 1 的申请,同时释放锁 2, t1 线程会完成它的任务
        if (t2.isAlive()) {
            t2.interrupt();
        }
    }
}

6.4 tryLock

tryLock    public boolean tryLock()

仅在调用时锁未被另一个线程保持的情况下,才获取该锁。 

1)如果该锁没有被另一个线程保持,并且立即返回 true 值,则将锁的保持计数设置为 1。
即使已将此锁设置为使用公平排序策略,但是调用 tryLock() 仍将 立即获取锁(如果有可用的),而不管其他线程当前是否正在等待该锁。
在某些情况下,此“闯入”行为可能很有用,即使它会打破公平性也如此。如果希望遵守此锁的公平设置,则使用 tryLock(0, TimeUnit.SECONDS) ,它几乎是等效的(也检测中断)。 

2)如果当前线程已经保持此锁,则将保持计数加 1,该方法将返回 true。 

3)如果锁被另一个线程保持,则此方法将立即返回 false 值。 

指定者:
   接口 Lock 中的  tryLock
返回: 
   如果锁是自由的并且被当前线程获取,或者当前线程已经保持该锁,则返回 true ;否则返回 false
作者:wuxinliulei
链接:https://www.zhihu.com/question/36771163/answer/68974735
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
// 仅在调用时锁是空闲的情况下才获取锁
boolean tryLock()

// 如果锁在给定的等待时间内是空闲的,并且当前线程没有被中断,则获取锁
boolean tryLock(long time, TimeUnit unit)
public class C2TryLock {
    private ArrayList arrayList = new ArrayList<>();
    //注意这个地方
    private Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        final C2TryLock test = new C2TryLock();

        Runnable run1 = new Runnable() {
            @Override
            public void run() {
                test.insert(Thread.currentThread());
            }
        };

        Runnable run2 = new Runnable() {
            @Override
            public void run() {
                try {
                    test.insert2(Thread.currentThread());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Runnable run3 = new Runnable() {
            @Override
            public void run() {
                test.threadSleep();
            }
        };
        
        // 情况1:开启线程3
        new Thread(run3, "线程3").start();
        
        // 情况2:不开启线程3,线程1执行中睡眠3秒
//        new Thread(run3, "线程3").start();
        
        // 情况3:线程1执行中睡眠2秒
        new Thread(run1, "线程1").start();
        new Thread(run2, "线程2").start();
    }

    public void insert(Thread thread) {
        // 如果锁被占用直接返回false
        if (lock.tryLock()) {
            try {
                System.out.println(thread.getName() + "得到了锁");
                Thread.sleep(2000);
                for (int i = 0; i < 5; i++) {
                    arrayList.add(i);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println(thread.getName() + "释放了锁");
                lock.unlock();
            }
        } else {
            System.out.println(thread.getName() + "获取锁失败");
        }
    }

    public void insert2(Thread thread) throws InterruptedException {
        // 如果锁被占用,在等待3秒内获得锁返回true,超出等待时间后返回false
        if (lock.tryLock(3, TimeUnit.SECONDS)) {
            try {
                System.out.println(thread.getName() + "得到了锁");
                for (int i = 0; i < 5; i++) {
                    arrayList.add(i);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println(thread.getName() + "释放了锁");
                lock.unlock();
            }
        } else {
            System.out.println(thread.getName() + "获取锁失败");
        }
    }

    public void threadSleep() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"睡眠中...");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

6.5 newCondition()方法

// 返回一个绑定到这个Lock实例的新的Condition实例
Condition newCondition()

??:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

await()会使当前线程等待,同时会释放锁,当其他线程调用 signal()时,线程会重新获得锁并继续执行。

注意:

在调用 Condition 的 await()/signal()方法前,也需要线程持有相关的 Lock 锁,调用 await()后线程会释放这个锁,在 singal()调用后会从当前 Condition 对象的等待队列中唤醒 一个线程,唤醒的线程尝试获得锁,一旦获得锁成功就继续执行。若调用await()/signal()方法前没有持有相关Lock锁,会抛出java.lang.IllegalMonitorStateException异常。

Interface Condition

Modifier and Type Method Description
void await() 使当前线程等待,直到它被通知或中断。
boolean await(long time, TimeUnit unit) 使当前线程等待,直到它被通知或中断,或指定的等待时间过去。
long awaitNanos(long nanosTimeout) 使当前线程等待,直到它被通知或中断,或指定的等待时间(纳秒)过去。
void awaitUninterruptibly() 使当前线程等待,直到它得到信号
boolean awaitUntil(Date deadline) 使当前线程等待,直到它被通知或被中断,或超过指定的截止日期。
void signal() 唤醒一个等待的线程。
void signalAll() 唤醒所有等待的线程。

举个??:

使用Condition实现两个线程交替打印字母和数字

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

/**
 * 使用Condition实现两个线程交替打印字母和数字
 *
 */
public class AlternatePrint {

    static Lock lock = new ReentrantLock();

    static Condition figureCondition = lock.newCondition();
    static Condition letterCondition = lock.newCondition();

    private int num = 0;


    public static void main(String[] args) {
        AlternatePrint alternatePrint = new AlternatePrint();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 26; i++) {
                    alternatePrint.printLetter();
                }
                System.out.println("thread-0结束");

                /*
                * 需要释放两个Condition等待的线程,
                *
                * 问题:Thread-0线程打印完'Z'后,唤醒了Thread-1线程自身进入等待并释放锁
                * Thread-1线程打印完26后唤醒Thread-0,自身进入等待并释放锁,自此Thread-1一致处于等待及获取锁的状态TIMED_WAITING
                *
                * 解决方式:
                * Thread-0线程结束后唤醒另一个Condition的线程等待状态,使程序正常结束
                * */
                alternatePrint.releaseWail();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 26; i++) {
                    alternatePrint.printFigure();
                }
                System.out.println("thread-1结束");
                alternatePrint.releaseWail();
            }
        }).start();
    }

    /**
     * 打印字母
     */
    private void printLetter() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"--->" + (char)(num + 65));
            // 这里要先把对方释放,自身再等待,否则当前持有锁的线程会一直等待,其他线程也不能获取锁
            figureCondition.signal();
            letterCondition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 打印数字
     */
    private void printFigure() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"--->" + (++num));
            // 这里要先把对方释放,自身再等待,否则当前持有锁的线程会一直等待,其他线程也不能获取锁
            letterCondition.signal();
            figureCondition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    private void releaseWail() {
        lock.lock();
        try {
            letterCondition.signalAll();
            figureCondition.signalAll();
        }  finally {
            lock.unlock();
        }
    }
}

运行结果:程序没有完全结束,其中一个线程处于等待状态

修改后:

6.6 公平锁与非公平锁

大多数情况下,锁的申请都是非公平的,如果线程 1与线程 2都在请求锁 A,当锁 A 可用时,系统只是会从阻塞队列中随机的选择一个线程,不能保证其公平性。

公平的锁会按照时间先后顺序,保证先到先得,公平锁的这一特点不会出现线程饥饿现象。

synchronized 内部锁就是非公平的,ReentrantLock 重入锁提供了一个构造方法:ReentrantLock(boolean fair) ,当在创建锁对象时实参传递 true 可以把该锁设置为公平锁。

// 创建一个ReentrantLock实例,非公平锁
ReentrantLock()

// 使用给定的公平策略创建一个ReentrantLock实例。
ReentrantLock(boolean fair)

公平锁看起来很公平,但是要实现公平锁必须要求系统维护一个有序队列,公平锁的实现成本较高,性能也低。因此默认情况下锁是非公平的,不是特别的需求,一般不使用公平锁。

/**
 * 公平锁与非公平锁
 */
public class Test01 {
    // static ReentrantLock lock = new ReentrantLock(); //默认是非公平锁
    static ReentrantLock lock = new ReentrantLock(true); //定义公平锁

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    lock.lock();
                    try {
                        System.out.println(Thread.currentThread().getName() + "获得了锁对象");
                    } finally {
                        lock.unlock();
                    }
                }
            }
        };
        for (int i = 0; i < 5; i++) {
            new Thread(runnable).start();
        }
        /*
            运行程序
            1)如果是非公平锁, 系统倾向于让一个线程再次获得已经持有的锁, 这种分配策略是高效的,非公平的
            2)如果是公平锁, 多个线程不会发生同一个线程连续多次获得锁的可能,保证了公平性
        */
    }
}

6.7 ReentrantLock中API

// 返回当前线程调用 lock()方法的次数
int getHoldCount()
    
// 返回正等待获得锁的线程预估数
int getQueueLength()
    
// 返回与 Condition 条件相关的等待的线程预估数
int getWaitQueueLength(Condition condition)
    
// 查询参数指定的线程是否在等待获得锁
boolean hasQueuedThread(Thread thread) 
    
//查询是否还有线程在等待获得该锁
boolean hasQueuedThreads() 
    
// 查询是否有线程正在等待指定的 Condition 条件
boolean hasWaiters(Condition condition) 

// 判断是否为公平锁
boolean isFair() 
    
// 判断当前线程是否持有该锁
boolean isHeldByCurrentThread() 
    
// 查询当前锁是否被线程持有
boolean isLocked()

6.8 ReentrantReadWriteLock 读写锁

synchronized 内部锁与 ReentrantLock 锁都是独占锁(排它锁),同一时间只允许一个线程执行同步代码块,可以保证线程的安全性,但是执行效率低。

ReentrantReadWriteLock 读写锁是一种改进的排他锁,也可以称作共享/排他锁。允许多个线程同时读取共享数据,但是一次只允许一个线程对共享数据进行更新。

读写锁通过读锁与写锁来完成读写操作,线程在读取共享数据前必须先持有读锁,该读锁可以同时被多个线程持有,即它是共享的线程在修改共享数据前必须先持有写锁,写锁是排他的。一个线程持有写锁时其他线程无法获得相应的锁(读/写锁)。

读锁只是在读线程之间共享任何一个线程持有读锁时,其他线程都无法获得写锁,保证线程在读取数据期间没有其他线程对数据进行更新,使得读线程能够读到数据的最新值,保证在读数据期间共享变量不被修改。

获得条件 排他性 作用
读锁 写锁未被任意线程持有 对读线程是共享的, 对写线程是排他的 允许多个读线程可以同时读取共享数据,保证在读共享数据时,没有其他线程对共享数据进行修改
写锁 该写锁未被其他线程持有,并且相应的读锁也未被其他线程持有 对读线程或者写线程都是排他的 保证写线程以独占的方式修改共享数据

读写锁允许读读共享,读写互斥,写写互斥。

在 java.util.concurrent.locks包中定义了 ReadWriteLock接口,该接口中定义了 readLock()返回读锁,定义 writeLock()方法返回写锁,该接口的实现类是 ReentrantReadWriteLock。

Interface ReadWriteLock

// 返回用于读取的锁。
Lock readLock() 

// 返回用于写入的锁。
Lock writeLock() 

Class ReentrantReadWriteLock Implemented ReadWriteLock

// 返回用于读取的锁。
ReentrantReadWriteLock.ReadLock  readLock() 

// 返回用于写入的锁。
ReentrantReadWriteLock.WriteLock  writeLock()  

注意:

readLock()与 writeLock()方法返回的锁对象是同一个锁的两个不同的角色,不是分别获得两个不同的锁。ReadWriteLock 接口实例可以充当两个角色,读写锁的其他使用方法

// 定义读写锁
ReadWriteLock rwLock = new ReentrantReadWriteLock();
// 获得读锁
Lock readLock = rwLock.readLock();
// 获得写锁
Lock writeLock = rwLock.writeLock();

/*
  读数据
*/
readLock.lock(); //申请读锁
try{
    // 读取共享数据
} finally {  
    // 总是在 finally 子句中释放锁
    readLock.unlock();
}

/*
  写数据
*/
writeLock.lock(); //申请写锁
try{
    // 更新修改共享数据
} finally {
    // 总是在 finally 子句中释放锁
    writeLock.unlock();
} 

6.8.1 读读共享

ReadWriteLock 读写锁可以实现多个线程同时读取共享数据,即读读共享,可以提高程序的读取数据的效率。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * ReadWriteLock 读写锁可以实现读读共享,允许多个线程同时获得读锁
 */
public class Test01 {
    static class Service {
        // 定义读写锁
        ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        // 定义方法读取数据
        public void read() {
            // 获得读锁
            readWriteLock.readLock().lock();
            try {
                System.out.println(Thread.currentThread().getName() + "获得读锁,开始读取数据的时间--" + System.currentTimeMillis());
                // 模拟读取数据用时3s
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 释放读锁
                readWriteLock.readLock().unlock();
            }
        }
    }

    public static void main(String[] args) {
        Service service = new Service();

        // 创建 5 个线程,调用 read()方法
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 在线程中调用 read()读取数据
                    service.read();
                }
            }).start();
        }
        // 运行程序后,多个线程几乎可以同时获得锁读,执行 lock()后面的代码
    }
}

6.8.2 写写互斥

通过 ReadWriteLock 读写锁中的写锁,只允许有一个线程执行 lock()后面的代码。即获取写锁得线程间使互斥得。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 演示 ReadWriteLock 的 writeLock()写锁是互斥的,只允许有一个线程持有
 */
public class Test02 {
    static class Service {
        // 先定义读写锁
        ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        // 定义方法修改数据
        public void write() {
            try {
                // 申请获得写锁
                readWriteLock.writeLock().lock();

                System.out.println(Thread.currentThread().getName() + "获得写锁,开始修改数据的时间--"
                        + DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now()));
                // 模拟修改数据的用时3s
                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName() + "修改数据完毕时的时间=="
                        + DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 释放写锁
                readWriteLock.writeLock().unlock();
            }
        }
    }

    public static void main(String[] args) {
        Service service = new Service();

        // 创建 5 个线程修改数据
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 调用修改数据的方法
                    service.write();
                }
            }).start();
        }
        
    }
}

6.8.3 读写互斥

写锁是独占锁、是排他锁,读线程与写线程也是互斥的。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


/**
 * 演示 ReadWriteLock 的读写互斥
 * 

* 一个线程获得读锁时,写线程等待; 一个线程获得写锁时,其他线程等待 */ public class Test03 { static class Service { //先定义读写锁 ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); // 获得读锁 Lock readLock = readWriteLock.readLock(); // 获得写锁 Lock writeLock = readWriteLock.writeLock(); /** * 读取共享数据 */ public void read() { readLock.lock(); try { System.out.println(Thread.currentThread().getName() + "获得读锁,开始读取数据的时间--" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") .format(LocalDateTime.now())); // 模拟读取数据的操作用时 Thread.sleep(3000); System.out.println(Thread.currentThread().getName() + "读取数据完毕时的时间==" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") .format(LocalDateTime.now())); } catch (InterruptedException e) { e.printStackTrace(); } finally { readLock.unlock(); } } //定义方法修改数据 /** * 更新共享数据 */ public void write() { writeLock.lock(); try { System.out.println(Thread.currentThread().getName() + "获得写锁,开始修改数据的时间--" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now())); // 模拟修改数据的用时 Thread.sleep(3000); System.out.println(Thread.currentThread().getName() + "修改数据完毕时的时间==" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now())); } catch (InterruptedException e) { e.printStackTrace(); } finally { writeLock.unlock(); } } } public static void main(String[] args) { Service service = new Service(); // 定义一个线程读数据 new Thread(new Runnable() { @Override public void run() { service.read(); } }).start(); // 定义一个线程写数据 new Thread(new Runnable() { @Override public void run() { service.write(); } }).start(); } }

6.9 Lock与Synchronized区别

1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
   synchronized关键字可以直接修饰方法,也可以修饰代码块,而lock只能修饰代码块
   
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。(提供tryLock)

5)Lock可以提高多个线程进行读操作的效率。(提供读写锁)

在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。