文章目录
说明
本博客每周五更新一次。
最近一次开发中使用多线程,过去常用synchronized,基于jvm实现,代码简答,可控性弱,高并发场景效率相对较低,以后尽量多用Lock实现。
Lock实现开始于java 1.5,java.util.concurrent.locks包下Lock和ReadWriteLock是两种类型锁的根接口。
分享
- 大数据博客列表
- 开发记录汇总
- 个人java工具库 项目https://gitee.com/wangzonghui/object-tool
- 包含json、string、集合、excel、zip压缩、pdf、bytes、http等多种工具,欢迎使用。
资料
synchronized与Lock对比
- synchronized不需要用户去手动释放锁,Lock需要使用者主动释放锁,否则会造成造成死锁。
- synchronized锁因为等待IO或sleep等愿意被阻塞,其他线程只能一直等待,Lock可设置等待时间。
- 多线程写操作会造成数据异常,读操作不会,synchronized锁不区分读写,限制单线程操作。Lock的ReentrantReadWriteLock支持读写区别管理,多线程(共享锁)读取,单线程(排他锁)写入,效率更高。
- synchronized无法判断线程是否成功获取到锁,Lock可获取锁状态。
Lock
Lock 接口方法
方法 | 说明 |
---|---|
void lock() | 获取锁,如果已被其他线程获取,则等待。 |
void lockInterruptibly() | 获取锁,如果已被其他线程获取,可调用interrupt()中断等待 |
Condition newCondition() | 返回绑定到此 Lock的新的Condition实例,用于线程间通信。 |
boolean tryLock() | 尝试获取该锁,返回状态 |
boolean tryLock(long time, TimeUnit unit) | 设定时间内尝试获取该锁,返回状态 |
void unlock() | 释放锁 |
实现类 ReentrantLock
- ReentrantLock为可重入锁,也是唯一实现Lock接口的类,可指定锁类型为 true 公平锁和 false 非公平锁,默认false非公平锁,使用实例如下:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockThread {
//创建锁对象
Lock lock = new ReentrantLock(); //不指定布尔类型,默认非公平锁
//同步方法
public void lock(String name) {
// 获取锁
lock.lock();
try {
System.out.println(name + " get the lock");
//lock() 必须释放锁,否则会堵塞,tryLock()尝试获取锁,并响应锁状态。
} finally {
// 释放锁
lock.unlock();
System.out.println(name + " release the lock");
}
}
public static void main(String[] args) {
LockThread lt = new LockThread();
new Thread(() -> lt.lock("A")).start();
new Thread(() -> lt.lock("B")).start();
}
}
ReadWriteLock
ReadWriteLock 接口方法
ReadWriteLock 维护了一对相关的锁:只读和写入。没有 writer时,读取锁可由多个 reader 线程同时操作,当有writer时,写入锁排他,读取操作挂起。
方法 | 说明 |
---|---|
Lock readLock() | 获取读操作锁 |
Lock writeLock() | 获取写操作锁 |
实现类 ReentrantReadWriteLock
- 代码实例,三个线程同时读写一个共享数据
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class Queue {
//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
private Object data = null;
//创建锁对象
ReadWriteLock lock = new ReentrantReadWriteLock();
Lock read=lock.readLock(); //读取锁
Lock write=lock.writeLock(); //写入锁
// 读数据
public void get() {
// 加读锁
read.lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to read data!");
Thread.sleep((long) (Math.random() * 1000)); //随机时间挂起
System.out.println(Thread.currentThread().getName() + " have read data :" + data);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放读锁
read.unlock();
}
}
// 写数据
public void put(Object data) {
// 加写锁
write.lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to write data!");
Thread.sleep((long) (Math.random() * 1000)); //随机时间挂起
this.data = data;
System.out.println(Thread.currentThread().getName() + " have write data: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放写锁
write.unlock();
}
}
}
public class ReadWriteLockDemo {
public static void main(String[] args) {
final Queue queue = new Queue();
//一共启动6个线程,3个读线程,3个写线程
for (int i = 0; i < 3; i++) {
//读线程
new Thread() {
public void run() {
while (true) {
queue.get();
}
}
}.start();
//写线程
new Thread() {
public void run() {
while (true) {
queue.put(new Random().nextInt(10000));
}
}
}.start();
}
}
}
总结
- 多线程一般用于提高效率,增加单位时间处理能力,设计上功能模块化,避免功能重叠,造成逻辑复杂,给自己挖坑,增加开发难度。
- 从功能可用,到实现功能且执行高效,到差异化可复用实现功能,逐步提升自我要求,技术没有最好,只有更好,加油。