Java Lock 显式锁案例|ReentrantLock 解决多线程安全问题(超详细实战)

一、案例需求

自定义数组集合,开启两个线程同时执行添加元素操作:

  • 线程 1:添加字符串 "F"
  • 线程 2:添加字符串 "e"

不做同步:会出现数据覆盖、索引错乱、线程安全问题 使用 ReentrantLock 显式锁 保证多线程原子操作,保证数据安全。

二、知识点铺垫

1. Lock 锁简介

  • Lockjava.util.concurrent.locks 包下的显式锁接口
  • 常用实现类:ReentrantLock(可重入锁)
  • 区别于 synchronized(隐式锁自动上锁解锁)
  • 手动上锁、手动解锁,控制粒度更精细、更灵活

2. 核心方法

  • lock():手动获取锁,锁被占用则阻塞等待
  • unlock():手动释放锁(必须放在 finally 中,保证一定释放)

三、完整可运行代码

复制代码
package Test_07;

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

/**
 * 本案例演示:ReentrantLock 显式锁解决多线程数据安全问题
 */
public class Test_03 {
	public static void main(String[] args) throws InterruptedException {
		// 共享资源对象(临界资源)
		MyList3 list = new MyList3();
		
		// 创建两个线程
		ThreadF t1 = new ThreadF(list);
		ThreadE t2 = new ThreadE(list);
		
		// 启动线程
		t1.start();
		t2.start();
		
		// 主线程等待两个子线程执行完毕
		t1.join();
		t2.join();
		
		// 最终遍历输出集合数据
		list.printList();
	}
}

// 线程1:添加元素 F
class ThreadF extends Thread {
	private MyList3 list;

	public ThreadF(MyList3 list) {
		this.list = list;
	}

	@Override
	public void run() {
		list.add("F");
	}
}

// 线程2:添加元素 e
class ThreadE extends Thread {
	private MyList3 list;

	public ThreadE(MyList3 list) {
		this.list = list;
	}

	@Override
	public void run() {
		list.add("e");
	}
}

// 自定义数组集合类(存在临界资源)
class MyList3 {
	// 底层数组容器
	private String[] str = {"A", "B", "", ""};
	// 元素下标计数器(临界资源,多线程共享)
	private int index = 2;

	// 创建显式锁对象(唯一锁对象,保证线程互斥)
	private Lock lock = new ReentrantLock();

	/**
	 * 加锁的添加方法
	 */
	public void add(String s) {
		// 1.手动上锁
		lock.lock();
		try {
			// 原子操作:赋值 + 休眠模拟业务耗时 + 索引自增
			str[index] = s;
			Thread.sleep(500);
			index++;
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			// 2.手动释放锁(finally 保证锁一定释放,不死锁)
			lock.unlock();
		}
	}

	/**
	 * 遍历打印集合所有元素
	 */
	public void printList() {
		System.out.println("数据中实际存储元素的个数:" + index);
		for (int i = 0; i < str.length; i++) {
			System.out.print(str[i] + " ");
		}
		System.out.println();
	}
}

四、代码核心解析

1. 临界资源

  • String[] str 数组容器
  • int index 共享索引计数器

两个线程同时操作这两个变量,属于多线程临界资源

2. 加锁原理

  1. 两个线程争抢 同一个 ReentrantLock 锁对象
  2. 抢到锁的线程进入 try 代码块执行完整业务
  3. 执行完毕进入 finally 强制释放锁
  4. 另一个线程才能进入执行

3. sleep 不释放锁

Thread.sleep(500)

  • 休眠只会释放 CPU 资源
  • 不会释放 Lock 锁
  • 保证整个添加操作是完整原子操作,不会被打断

五、运行结果

复制代码
数据中实际存储元素的个数:4
A B F e 

结果说明

  • 两个线程依次执行,没有数据覆盖
  • 索引正常递增,数据完整有序
  • 线程同步成功,解决并发安全问题

六、如果不加锁会出现什么问题?

  1. 两个线程同时赋值同一个下标
  2. 数据覆盖、丢失数据
  3. index 错乱,元素个数统计错误
  4. 多线程并发安全问题

七、Lock 锁 VS synchronized 面试总结

  1. synchronized:隐式锁,自动上锁、自动解锁,语法简单
  2. Lock(ReentrantLock):显式锁,手动上锁、手动解锁
  3. Lock 灵活性更高、可重试、可超时、可公平锁
  4. Lock 必须在 finally 解锁,防止死锁

八、核心考点背诵

  1. ReentrantLock 属于 java.util.concurrent.locks
  2. Lock 是显式锁,需要手动 lock ()、unlock ()
  3. unlock () 必须放在 finally 中,保证锁释放
  4. sleep 不释放锁、保证原子操作完整性
  5. 多线程必须使用同一个锁对象才能实现同步
相关推荐
职豚求职小程序44 分钟前
中国人寿27秋招笔试[特殊字符]北森机考|备考攻略
笔记
步行cgn1 小时前
Spring 中完整的 Scope 选项详解
java·后端·spring
江湖人称菠萝包1 小时前
【Qt】《Qt 5.9 C++开发指南》笔记-Chapter9-Qt Charts
笔记·qt·qt5
乐橙开放平台1 小时前
从「多套客户端」到一套开放能力:乐橙视频监控能力复盘
笔记·物联网·自动化·音视频·智能家居
hai_android1 小时前
Android MeasureSpec 详解
android·java·kotlin
倍利福猎头公司官方账号1 小时前
2026具身智能赛道还火热吗?机器人人选该如何思考下半年的工作机会?
人工智能·面试·职场和发展·机器人·求职招聘
山岚的运维笔记2 小时前
mysql 专业笔记 -- 第 36 章:MySQL 管理
运维·数据库·笔记·后端·mysql·oracle·dba
阳光宅男@李光熠3 小时前
【电子通识】一起学习TDK的EMC基础——电池兼容设计方法概述
java·前端·数据库
张宜强3 小时前
025 安居租赁 · 房屋租赁平台
java·课程设计