授课语音

Java锁的应用

1. 介绍

Java的锁机制是并发编程的重要组成部分,它确保了多个线程对共享资源的访问是互斥的,从而避免数据不一致的问题。锁可以作用于方法、代码块、对象等,锁的粒度越细,竞争越少,但复杂度也会增加。

内置锁synchronized,由JVM实现,主要分为两类:

  • 对象锁:通过synchronized(object)修饰实例方法或代码块,确保同一时刻只有一个线程可以访问该对象的同步区域。
  • 类锁:通过synchronized(ClassName.class)修饰静态方法或代码块,确保同一时刻只有一个线程可以访问该类的同步区域。

特点:

  • 同一时间只有一个线程可以获得锁(互斥)。
  • 获得锁的线程可以多次获得同一把锁(可重入)。
  • 未获得锁的线程会被阻塞,直到锁被释放(阻塞)。
  • 锁的获取和释放由JVM自动完成。
  • 它不支持锁中断,也无法判断锁的状态,适合简单的并发场景。

显示锁Lock,由Java提供,分为:

  • 公平锁:当锁释放时,先申请的线程先获取锁。
  • 非公平锁:当锁释放时,后来的线程有可能先获取到锁,不按队列顺序。

特点:

  • 锁的获取和释放都是手动控制,包括lock()unlock()方法。
  • 支持可中断的锁请求(lockInterruptibly())。
  • 可以判断锁的状态(tryLock())。

图示

Image___1397757896013925625===441faceb05ef6b7e72a0300f8ad43c1e===3_1.png___

ReentrantLock 是显示锁的实现,提供了更高级的锁控制。默认是非公平锁,支持以下操作:

  • tryLock():尝试获取锁。
  • tryLock(long timeout, TimeUnit unit):支持超时机制。
  • Condition:实现等待通知机制。

ReentrantReadWriteLock

  • 读锁:允许多个线程同时持有,只要没有线程持有写锁。
  • 写锁:独占锁,当一个线程持有写锁时,其他线程无法获得读锁或写锁,确保写的原子性。

StampedLock

  • 优化了读的场景,提供乐观读锁(CAS机制),不支持Condition,适用于读多写少的场景。

volatile修饰符

  • 是一种轻量级的同步机制,确保变量在多个线程间的可见性。
  • 一个线程修改了volatile变量,其他线程能立即看到该修改。
  • volatile变量的读写操作不会被JVM重排序,保证操作的顺序性。
  • 常用于单例模式的实例化、状态标识等场景。

锁的性能优化

  • CAS(Compare and Swap):乐观锁机制,是一种原子操作,避免了线程阻塞,通过不断重试实现同步。但不适合高竞争的场景。
  • 自旋锁:当锁的竞争较少时,通过自旋可以减少线程挂起的开销,依赖CAS操作。
  • 偏向锁:偏向第一个获取锁的线程,后续该线程不需要同步操作;适合只有一个线程访问的场景,但实现复杂,JDK 18之后已被废弃。
  • 轻量级锁:无竞争情况下通过CAS获取锁;一旦有竞争,线程会自旋等待。
  • 重量级锁:传统锁机制,获取失败时线程阻塞,直到锁释放。锁会有升级操作,一旦升级为重量级锁,就不能降级。

await和signal

  • awaitjava.util.concurrent.locks.Condition接口的方法,线程等待直到被唤醒,必须在lock()unlock()之间调用。
  • signal用于唤醒等待的线程,signalAll则唤醒所有等待在condition条件上的线程。

sleep

  • Thread中的静态方法,使得线程暂停一段时间,用于延迟操作。

图示

Image___1397757896014786380===4ad01c0dbb46c04ae1da9d4ff90385d4===3_2.png___

2. 代码案例

package com.zhilitech.lock;

import java.util.concurrent.*;
import java.util.concurrent.locks.*;

public class LockExample {

    // 对象锁示例
    private static class ObjectLockCounter {
        private int count = 0; // 计数器变量

        // 增加计数器的方法,使用 synchronized 锁住整个方法(对象锁)
        public synchronized void increment() {
            count++; // 增加计数
        }

        // 获取计数器的值,使用 synchronized 锁住整个方法(对象锁)
        public synchronized int getCount() {
            return count; // 返回计数
        }
    }

    // 类锁示例
    private static class ClassLockCounter {
        private static int count = 0; // 静态计数器变量

        // 增加计数器的方法,使用 synchronized 锁住整个方法(类锁)
        public static synchronized void increment() {
            count++; // 增加计数
        }

        // 获取计数器的值,使用 synchronized 锁住整个方法(类锁)
        public static synchronized int getCount() {
            return count; // 返回计数
        }
    }

    // volatile 示例
    private static class VolatileCounter {
        private volatile int count = 0; // volatile计数器变量

        // 增加计数器的方法
        public void increment() {
            count++; // 增加计数
        }

        // 获取计数器的值
        public int getCount() {
            return count; // 返回计数
        }
    }

    // ReentrantReadWriteLock 示例
    private static class ReadWriteLockCounter {
        private int count = 0; // 计数器变量
        private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); // 读写锁对象
        private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); // 读锁
        private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); // 写锁

        // 增加计数器的方法,使用 WriteLock 锁住代码块
        public void increment() {
            writeLock.lock(); // 获取写锁
            try {
                count++; // 增加计数
            } finally {
                writeLock.unlock(); // 释放写锁
            }
        }

        // 获取计数器的值,使用 ReadLock 锁住代码块
        public int getCount() {
            readLock.lock(); // 获取读锁
            try {
                return count; // 返回计数
            } finally {
                readLock.unlock(); // 释放读锁
            }
        }
    }

    // ReentrantLock 超时示例
    private static class TimeoutLockCounter {
        private int count = 0; // 计数器变量
        private final ReentrantLock lock = new ReentrantLock(); // ReentrantLock对象

        // 增加计数器的方法,使用 ReentrantLock 锁住代码块,并设定超时时间
        public void increment() {
            boolean acquired = false; // 锁获取标志
            try {
                acquired = lock.tryLock(100, TimeUnit.MILLISECONDS); // 尝试获取锁,设定超时
                if (acquired) {
                    count++; // 增加计数
                } else {
                    System.out.println("未能获取锁,跳过增加");
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 中断处理
                System.out.println("线程被中断");
            } finally {
                if (acquired) {
                    lock.unlock(); // 释放锁
                }
            }
        }

        // 获取计数器的值,使用 ReentrantLock 锁住代码块
        public int getCount() {
            lock.lock(); // 获取锁
            try {
                return count; // 返回计数
            } finally {
                lock.unlock(); // 释放锁
            }
        }
    }

    // Condition 示例 - 生产者消费者模式
    private static class ProducerConsumer {
        private final ReentrantLock lock = new ReentrantLock(); // ReentrantLock对象
        private final Condition notEmpty = lock.newCondition(); // 不为空条件
        private final Condition notFull = lock.newCondition(); // 不满条件
        private final int[] buffer = new int[10]; // 缓冲区
        private int count, putIndex, takeIndex; // 计数、放置索引、取出索引

        // 生产者线程
        public void produce(int item) throws InterruptedException {
            lock.lock(); // 获取锁
            try {
                while (count == buffer.length) {
                    notFull.await(); // 缓冲区满时,等待
                }
                buffer[putIndex] = item; // 放入产品
                putIndex = (putIndex + 1) % buffer.length; // 更新放置索引
                count++; // 更新计数
                notEmpty.signal(); // 唤醒消费者
            } finally {
                lock.unlock(); // 释放锁
            }
        }

        // 消费者线程
        public int consume() throws InterruptedException {
            lock.lock(); //

 获取锁
            try {
                while (count == 0) {
                    notEmpty.await(); // 缓冲区空时,等待
                }
                int item = buffer[takeIndex]; // 取出产品
                takeIndex = (takeIndex + 1) % buffer.length; // 更新取出索引
                count--; // 更新计数
                notFull.signal(); // 唤醒生产者
                return item; // 返回取出的产品
            } finally {
                lock.unlock(); // 释放锁
            }
        }
    }

    public static void main(String[] args) {
        // 测试 ObjectLockCounter(对象锁)
        ObjectLockCounter objectLockCounter = new ObjectLockCounter();
        System.out.println("测试 ObjectLockCounter(对象锁)");
        runTest(objectLockCounter);

        // 测试 ClassLockCounter(类锁)
        System.out.println("测试 ClassLockCounter(类锁)");
        runStaticTest();

        // 测试 VolatileCounter
        System.out.println("测试 VolatileCounter");
        VolatileCounter volatileCounter = new VolatileCounter();
        runTest(volatileCounter);

        // 测试 ReadWriteLockCounter(读写锁)
        System.out.println("ReadWriteLockCounter(读写锁)");
        ReadWriteLockCounter readWriteLockCounter = new ReadWriteLockCounter();
        runTest(readWriteLockCounter);

        // 测试 TimeoutLockCounter(带超时的 ReentrantLock)
        System.out.println("测试 TimeoutLockCounter(带超时的 ReentrantLock)");
        TimeoutLockCounter timeoutLockCounter = new TimeoutLockCounter();
        runTest(timeoutLockCounter);

        // 测试 ProducerConsumer(Condition 示例)
        System.out.println("测试 ProducerConsumer(Condition 示例)");
        ProducerConsumer producerConsumer = new ProducerConsumer();
        runProducerConsumerTest(producerConsumer);
    }

    // 运行测试的方法
    private static void runTest(Object counter) {
        Runnable incrementTask = () -> {
            for (int i = 0; i < 10; i++) {
                if (counter instanceof ObjectLockCounter) {
                    ((ObjectLockCounter) counter).increment(); // 增加计数
                } else if (counter instanceof VolatileCounter) {
                    ((VolatileCounter) counter).increment(); // 增加计数
                } else if (counter instanceof ReadWriteLockCounter) {
                    ((ReadWriteLockCounter) counter).increment(); // 增加计数
                } else if (counter instanceof TimeoutLockCounter) {
                    ((TimeoutLockCounter) counter).increment(); // 增加计数
                }
            }
        };

        Runnable readTask = () -> {
            for (int i = 0; i < 10; i++) {
                int count = 0;
                if (counter instanceof ObjectLockCounter) {
                    count = ((ObjectLockCounter) counter).getCount(); // 获取计数
                } else if (counter instanceof VolatileCounter) {
                    count = ((VolatileCounter) counter).getCount(); // 获取计数
                } else if (counter instanceof ReadWriteLockCounter) {
                    count = ((ReadWriteLockCounter) counter).getCount(); // 获取计数
                } else if (counter instanceof TimeoutLockCounter) {
                    count = ((TimeoutLockCounter) counter).getCount(); // 获取计数
                }
                System.out.println(Thread.currentThread().getName() + "读取Count: " + count); // 输出计数
            }
        };

        Thread incrementThread1 = new Thread(incrementTask); // 增加计数线程1
        Thread incrementThread2 = new Thread(incrementTask); // 增加计数线程2
        Thread readThread1 = new Thread(readTask); // 读取计数线程1
        Thread readThread2 = new Thread(readTask); // 读取计数线程2

        incrementThread1.start(); // 启动增加计数线程1
        incrementThread2.start(); // 启动增加计数线程2
        readThread1.start(); // 启动读取计数线程1
        readThread2.start(); // 启动读取计数线程2

        try {
            incrementThread1.join(); // 等待增加计数线程1结束
            incrementThread2.join(); // 等待增加计数线程2结束
            readThread1.join(); // 等待读取计数线程1结束
            readThread2.join(); // 等待读取计数线程2结束
        } catch (InterruptedException e) {
            e.printStackTrace(); // 异常处理
        }
    }

    // 运行类锁测试的方法
    private static void runStaticTest() {
        Runnable incrementTask = () -> {
            for (int i = 0; i < 10; i++) {
                ClassLockCounter.increment(); // 增加计数
            }
        };

        Runnable readTask = () -> {
            for (int i = 0; i < 10; i++) {
                int count = ClassLockCounter.getCount(); // 获取计数
                System.out.println(Thread.currentThread().getName() + "读取Count: " + count); // 输出计数
            }
        };

        Thread incrementThread1 = new Thread(incrementTask); // 增加计数线程1
        Thread incrementThread2 = new Thread(incrementTask); // 增加计数线程2
        Thread readThread1 = new Thread(readTask); // 读取计数线程1
        Thread readThread2 = new Thread(readTask); // 读取计数线程2

        incrementThread1.start(); // 启动增加计数线程1
        incrementThread2.start(); // 启动增加计数线程2
        readThread1.start(); // 启动读取计数线程1
        readThread2.start(); // 启动读取计数线程2

        try {
            incrementThread1.join(); // 等待增加计数线程1结束
            incrementThread2.join(); // 等待增加计数线程2结束
            readThread1.join(); // 等待读取计数线程1结束
            readThread2.join(); // 等待读取计数线程2结束
        } catch (InterruptedException e) {
            e.printStackTrace(); // 异常处理
        }
    }

    // 运行生产者消费者测试的方法
    private static void runProducerConsumerTest(ProducerConsumer producerConsumer) {
        Runnable producerTask = () -> {
            for (int i = 0; i < 10; i++) {
                try {
                    producerConsumer.produce(i); // 生产产品
                    System.out.println(Thread.currentThread().getName() + "生产者: " + i); // 输出生产的产品
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // 中断处理
                }
            }
        };

        Runnable consumerTask = () -> {
            for (int i = 0; i < 10; i++) {
                try {
                    int item = producerConsumer.consume(); // 消费产品
                    System.out.println(Thread.currentThread().getName() + "消费者: " + item); // 输出消费的产品
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // 中断处理
                }
            }
        };

        Thread producerThread = new Thread(producerTask); // 生产者线程
        Thread consumerThread = new Thread(consumerTask); // 消费者线程

        producerThread.start(); // 启动生产者线程
        consumerThread.start(); // 启动消费者线程

        try {
            producerThread.join(); // 等待生产者线程结束
            consumerThread.join(); // 等待消费者线程结束
        } catch (InterruptedException e) {
            e.printStackTrace(); // 异常处理
        }
    }
}

以上是一个完整的Java锁的应用示例,包含了各种锁的使用和相应的注释,以便理解每一部分的作用。希望这些内容能够帮助你更好地理解Java中的锁机制及其应用。

去1:1私密咨询

系列课程: