从零学Java Lock 接口

Java Lock 接口

文章目录

  • Java Lock 接口
    • Lock 接口的实现类
      • ReentrantLock - 重入锁
    • ReadWriteLock接口 - 读写锁
      • ReentrantReadWriteLock 实现类
    • Condition-条件队列
    • synchronized和Lock区别

JDK5加入,与synchronized比较,显示定义,结构更灵活。提供更多实用性方法,功能更强大、性能更优越。

常用方法

  • void lock() //获取锁,如锁被占用,则等待。
  • boolean tryLock() //尝试获取锁(成功返回true。失败返回false,不阻塞)
  • void unlock() //释放锁

Lock 接口的实现类

ReentrantLock - 重入锁

Lock接口的实现类,与synchronized一样具有互斥锁功能。

eg:

窗口卖票问题

TestReentrantLock:

package StageOne.day21.demo01;

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

/**
 * @author 胡昊龙
 * @version 1.0
 * @description: TODO
 * @date 2024/1/16 9:22
 */
public class TestReentrantLock {
    public static void main(String[] args) {
        //使用ReentrantLock实现线程同步
        //1.创建线程池
        ExecutorService es = Executors.newCachedThreadPool();
        //2.提交任务
        Runnable ticket = new Runnable() {
            private int count = 1000;
            //创建重入锁
            private Lock lock = new ReentrantLock();
            @Override
            public void run() {
                while (true) {
                    //上锁
                    lock.lock();
                    try {
                        if (count<=0) {
                            break;
                        }
                        System.out.println(
                            Thread.currentThread().getName()+"卖了第"+count+"张票"
                        );
                        count--;
                    } finally {
                        //解锁
                        lock.unlock();
                    }
                }
            }
        };
        for (int i = 0; i < 4; i++) {
            es.submit(ticket);
        }
        //3.关闭线程池
        es.shutdown();
    }
}

ReadWriteLock接口 - 读写锁

ReentrantReadWriteLock 实现类

  • 一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁。
  • 支持多次分配读锁,使多个读操作可以并发执行。

从零学Java Lock 接口_第1张图片

互斥规则

  • 写-写:互斥,阻塞。
  • 读-写:互斥,读阻塞写、写阻塞读。
  • 读-读:不互斥、不阻塞。

在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率。

eg:

public class TestReentrantReadWriteLock {
    public static void main(String[] args) {
        ReadWrite rw = new ReadWrite();
        ExecutorService es = Executors.newCachedThreadPool();
        //提交任务
        long start = System.currentTimeMillis();
        for (int i = 0; i < 18; i++) {
            es.submit(new Runnable() {
                @Override
                public void run() {
                    rw.read();
                }
            });
        }

        for (int i = 0; i < 2; i++) {
            es.submit(new Runnable() {
                @Override
                public void run() {
                    rw.write(new Random().nextInt(100));
                }
            });
        }
        es.shutdown();
        //直到线程中任务全部完成, 再执行后续操作
        while (!es.isTerminated());
        long end = System.currentTimeMillis();
        System.out.println("用时:"+(end-start));
    }

    static class ReadWrite {
        private int value;
        private final ReadWriteLock lock = new ReentrantReadWriteLock();

        public void write(int value) {
            //上写锁
            lock.writeLock().lock();
            this.value = value;
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+"写入..."+value);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                //解锁
                lock.writeLock().unlock();
            }
        }

        public void read() {
            //上读锁
            lock.readLock().lock();
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+"读取..."+value);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.readLock().unlock();
            }
        }
    }
}

Condition-条件队列

Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式。

Condition可以通俗的理解为条件队列。当一个线程在调用了await方法以后进入等待队列,直到线程等待的某个条件为真的时候才会被唤醒。

方法名 描述
await() 当前线程进入等待状态
signal() 唤醒一个等待线程
signalAll() 唤醒所有等待线程

面试题:

需求:使用Lock和Condition实现三个线程交替输出20遍“ABC”

Alternative:

package StageOne.day21.demo01.TestAlter;

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

/**
 * @author 胡昊龙
 * @version 1.0
 * @description: TODO
 * @date 2024/1/16 11:19
 */
public class Alternative {
    private final Lock lock = new ReentrantLock();
    private final Condition conditionA = lock.newCondition();
    private final Condition conditionB = lock.newCondition();
    private final Condition conditionC = lock.newCondition();
    /**
     * 1,2,3 控制 该打印哪个字母
     */
    private int num = 1;

    public void printA() {
        lock.lock();
        try {
            if (num != 1) {
                conditionA.await();
            }
            System.out.print("A ");
            num=2;
            conditionB.signal();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    public void printB() {
        lock.lock();
        try {
            if (num != 2) {
                conditionB.await();
            }
            System.out.print("B ");
            num=3;
            conditionC.signal();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            if (num != 3) {
                conditionC.await();
            }
            System.out.println("C");
            System.out.println("--------");
            num=1;
            conditionA.signal();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
}

Test:

package StageOne.day21.demo01.TestAlter;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author 胡昊龙
 * @version 1.0
 * @description: TODO
 * @date 2024/1/16 11:25
 */
public class Test {
    public static void main(String[] args) {
        Alternative alternative = new Alternative();
        ExecutorService es = Executors.newCachedThreadPool();

        es.submit(() -> {
            for (int i = 0; i < 20; i++) {
                alternative.printA();
            }
        });

        es.submit(() -> {
            for (int i = 0; i < 20; i++) {
                alternative.printB();
            }
        });

        es.submit(() -> {
            for (int i = 0; i < 20; i++) {
                alternative.printC();
            }
        });

        es.shutdown();
    }
}

synchronized和Lock区别

类别 synchronized Lock
存在层次 Java的关键字,在JVM层面上 是一个接口、类
锁释放 1、获取锁的线程执行完同步代码,释放锁
2、线程执行发生异常,JVM会让线程释放锁
在finally中必须释放锁,不然容易造成线程死锁。
锁的获取 假设A线程获得锁,B线程等待。
如果A线程阻塞,B线程会一直等待
Lock可以尝试获得锁,线程可以不用一直等待。
锁状态 无法判断 可以判断
锁类型 可重入、不可中断、非公平 可重入、可中断、可公平或非公平

你可能感兴趣的:(从零学Java,java,python,开发语言)