1.Lock接口的三个实现类
- ReentrantLock(可重入锁)
- ReadLock(读锁)
- WriteLock(写锁)
2.自定义互斥锁实现
- 通过实现Lock接口的lock()方法和unlock()方法来完成自定义实现
- ps:主要思想就是设置一个状态量,根据状态量来判断是否锁被占用,如果被占用就wait(),如果没被占用就使用。
- 自定义互斥锁实现
public class MyLock implements Lock { /** * 是否拥有锁 */ private volatile Boolean isHoldLock = false; /** * 同一时刻,有且仅有一个线程拥有锁, * 其他线程只有等待拥有锁的线程释放锁才能获取锁 */ @Override public synchronized void lock() { // 通过自旋的方式去判断是否拥有锁,没有锁就进入线程等待 while (isHoldLock) { try { wait (); } catch (InterruptedException e) { e.printStackTrace (); } } // 设置线程拥有锁 isHoldLock = true; } /** * 释放锁 */ @Override public synchronized void unlock() { // 随机唤醒一个等待的线程 notify (); // 将该持有锁释放 isHoldLock = false; } // 忽略以下重写代码 }
- 测试代码
public class LockDemo { private static Lock lock = new MyLock (); private static CountDownLatch countDownLatch = new CountDownLatch (10); private static int num = 0; public static void inCreate() { // 获取锁 lock.lock (); num++; // 释放锁 lock.unlock (); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread (() -> { for (int j = 0; j < 1000000; j++) { inCreate (); } // 线程执行结束执行countDown,对计数减一 countDownLatch.countDown (); }).start (); } while (true) { // 所有线程执行完毕,输出num if (countDownLatch.getCount () == 0) { System.out.println (num); break; } } } }
控制台输出:
- 10000000
3.自定义重入锁实现
- ps:可重入锁的实现就是在互斥锁的基础上加上线程判断,并且使用一个reentryCount来记录锁的层数,以便于释放锁时的判断。
- 自定义重入锁实现
public class MyLock implements Lock { /** * 是否拥有锁 */ private volatile Boolean isHoldLock = false; /** * 当前拥有锁的线程 */ private Thread holdLockThread = null; /** * 重入计数 */ private int reentryCount = 0; /** * 同一时刻,有且仅有一个线程拥有锁, * 其他线程只有等待拥有锁的线程释放锁才能获取锁 */ @Override public synchronized void lock() { // 通过自旋的方式去判断是否拥有锁,并且是否被当前线程持有,否则就进入线程等待 while (isHoldLock && Thread.currentThread () != holdLockThread) { try { wait (); } catch (InterruptedException e) { e.printStackTrace (); } } // 设置线程拥有锁 isHoldLock = true; // 给当前线程赋值给holdLockThread holdLockThread = Thread.currentThread (); // 重入次数自增 reentryCount++; } /** * 释放锁 */ @Override public synchronized void unlock() { // 判断当前线程是否是持有锁的线程,是,重入次数减去1,不是就不做处理 if (holdLockThread == Thread.currentThread ()) { reentryCount--; if (reentryCount == 0) { // 随机唤醒一个等待的线程 notify (); // 将该持有锁释放 isHoldLock = false; } } } // 忽略其他重写方法 }
- 测试代码
public class ReentryDemo { private Lock lock = new MyLock (); public void methodA(){ lock.lock (); System.out.println (String.format ("线程%s进入方法A",Thread.currentThread ().getName ())); methodB (); lock.unlock (); } public void methodB(){ lock.lock (); System.out.println (String.format ("线程%s进入方法B",Thread.currentThread ().getName ())); lock.unlock (); } public static void main(String[] args) { new ReentryDemo ().methodA (); } }
控制台输出:
- 线程main进入方法A
- 线程main进入方法B
ps:可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁,叫可重入锁
4.总结
- ps:通过实现JDK提供的Lock接口,来自定义锁的实现,可以根据自己的业务要求编写,一般情况下使用ReentrantLock就可以了
- 互斥锁:主要思想就是设置一个状态量,根据状态量来判断是否锁被占用,如果被占用就wait(),如果没被占用就使用
- 重入锁:可重入锁的实现就是在互斥锁的基础上加上线程判断,并且使用一个reentryCount来记录锁的层数,以便于释放锁时的判断。
链接:java多线程:ReentrantLock的使用,实现共享变量线程安全