**1. java.util.concurrent.ReentrantLock**
ReentrantLock 类也可以实现线程同步。使用ReentrantLock的基本结构如下:
```
Lock myLock = new ReentrantLock();
myLock.lock(); // 加锁
try {
// 共享资源任务代码
} finally {
myLock.unlock(); // 解锁,一定要在finally块中解锁
}
```
* 该锁以串行方式提供服务,即线程需要排队等待被运行;
* `lock`方法和`unlock`方法必须配对使用,因为线程要调用`lock`加锁,调用`unlock`来解锁。
* `unlock`方法必须放在`finally`语句块中,因为防止当`try`语句块中抛出异常时,导致锁不能被释放,造成其他线程永远被阻塞。
* 该锁不允许使用带有资源的`try`语句。
* 需要注意因为`try`语句块抛出异常而导致提前退出`try`语句块,这样的话`finally`将提前解锁,使对象可能处于一种受损状态。
**2. ReentrantLock 锁可重入**
```
public void transfer(int from, int to, double amount) throws InterruptedException {
myLock.lock();
try {
// 资源共享代码
}
getTotalBanlance(); // 调用方法,该方法也使用了ReentrantLock锁
} finally {
mykLock.unlock();
}
}
public double getTotalBanlance() {
myLock.lock();
try {
// 资源共享代码
} finally {
myLock.unlock();
}
}
```
上面的代码中`transfer`方法调用了`getTotalBanlance`方法,而且两个方法都调用了`myLock.lock()`和`myLock.unlock()`,这是嵌套使用了ReentrantLock锁。
<br/>
ReentrantLock锁有一个持有计数器,上面代码中嵌套使用了两对`lock`和`unlock`,持有计数器就是2。当退出`getTotalBanlance`方法,持有计数器为1,再退出`transfer`方法,持有计数器为0,这就是可重入。
<br/>
**3. 案例演示**
使用`for`创建10个线程,这10个线程共同让变量`sum`进行递增。比较使用同步代码块与不适用同步代码块的区别。
```java
public class ReentrantLockTest {
static int sum = 0;
// 创建锁对象
private static Lock myLock = new ReentrantLock();
/**
* 同步块
*/
public static void userReentrantLock() {
// 使用for创建10个线程
for (int i = 1; i <= 10; i++) {
Runnable r = () -> {
try {
while (true) {
myLock.lock(); // 加锁
try {
++sum;
System.out.println(Thread.currentThread().getName() + " sum=" + sum);
Thread.sleep(100);
} finally {
myLock.unlock(); // 解锁
}
}
} catch (InterruptedException e) {
}
};
Thread t = new Thread(r);
t.setName("线程" + i);
t.start(); // 启动线程
}
}
/**
* 不使用同步块
*/
public static void noReentrantLock() {
// 使用for创建10个线程
for (int i = 1; i <= 10; i++) {
Runnable r = () -> {
try {
while (true) {
++sum;
System.out.println(Thread.currentThread().getName() + " sum=" + sum);
Thread.sleep(100);
}
} catch (InterruptedException e) {
}
};
Thread t = new Thread(r);
t.setName("线程" + i);
t.start(); // 启动线程
}
}
public static void main(String[] args) {
//让这两个方法分别单独运行,不要一起运行
// userReentrantLock();
noReentrantLock();
}
}
```
运行结果如下:
```
同步块结果-递增 不使用同步块结果-乱
线程1 sum=55 线程6 sum=11
线程1 sum=56 线程8 sum=13
线程1 sum=57 线程8 sum=13
线程1 sum=58 线程10 sum=14
线程1 sum=59 线程7 sum=15
线程3 sum=60 线程3 sum=16
线程3 sum=61 线程6 sum=18
线程2 sum=62 线程4 sum=16
线程2 sum=63 线程5 sum=16
```
结果递增,说明资源访问并不冲突。
结果乱,说明资源访问冲突了。
* java.util.concurrent.locks.Lock 5.0
* void lock()
对共享资源部分加锁。
* void unlock()
对共享资源部分解锁。
* java.util.concurrent.locks.ReentrantLock 5.0
* ReentrantLock()
构建一个可以被用来保护临界区的可重入锁。
* ReentrantLock(boolean fair)
构建一个可以被用来保护临界区的公平策略锁。它偏爱等待时间最长的线程,但是这个策略导致性能大大下降,其实这并不公平。默认情况不强制为公平。
- 网络通信
- 网络协议
- 端口和套接字
- TCP网络程序
- UDP网络程序
- 多线程聊天室
- 多线程
- 线程相关概念
- 线程实现方式
- 中断线程
- 线程生命周期
- 线程优先级
- 优先级规则
- 案例演示
- 线程同步机制
- 线程同步机制
- synchronized关键字
- ReentrantLock类
- Condition类
- 监视器概念
- volatile关键字
- final变量
- 死锁
- 线程局部变量
- 读/写锁
- 原子类
- 阻塞队列
- 工作规则
- 案例演示
- 常用阻塞队列
- 线程安全集合
- 高效的映射/集/队列
- 并发集视图
- 写数组的拷贝
- Arrays类的并行数组算法
- 同步包装器
- Callable与Future
- 执行器
- 线程池
- 预定执行
- 控制任务组
- Fork-Join框架
- 同步器
- 同步器
- 信号量
- CountDownLatch类
- CyclicBarrier类
- Exchanger类
- SynchronousQueue类
- 线程与Swing
- Swing与线程问题
- 两个原则
- Swing工作线程
- 单一线程规则
- 文件IO
- File类
- 文件输入输出
- ZIP压缩文件
- 集合
- 集合框架
- 集合接口
- 集合实现类
- 线程安全集合
- 集合算法
- 迭代器
- 集合排序
- JDBC
- JDBC是什么
- JDBC-ODBC桥
- JDBC驱动程序类型
- JDBC常用类与接口
- 数据库操作
- 连接数据库
- 增/删/改/查/预处理
- 事务
- 批处理
- commons-dbutils工具
- 安全问题
- Jedis
- 使用Jedis操作Redis数据库
- JSON转换
- 使用连接池
- 案例
- 单例破坏
- 单例定义
- 单例实现方式
- 懒汉式实现单例
- 饿汉式实现单例
- 单例破坏
- 类的单例破坏
- 枚举的单例破坏
- 克隆
- 克隆是什么
- 浅克隆
- 深克隆
- 注解
- 注解是什么
- 三大注解
- 内置注解
- 元注解
- 自定义注解
- NIO
- 相关概念
- BIO/NIO/AIO
- 多线程编程
- 线程同步
- 线程通信
- NIO
- NIO三大核心组件
- NIO网络编程
- NIO文件读写
- AIO
- Java8新特性
- Lambda表达式
- 方法引用
- 函数式接口
- 默认方法
- 什么是默认方法
- 默认方法语法格式
- 多个同名的默认方法问题
- 静态默认方法
- 默认方法实例
- Stream
- Stream是什么
- Stream示例
- Optional容器
- 新的日期时间API
- Base64
- SPI
- SPI是什么
- SPI与API的区别
- 常见场景
- 使用SPI需遵循的约定
- SPI使用步骤