文章目录
Java Lock锁
概述
synchronized被称为隐式锁,会自动获取释放锁,是非公平锁;Lock被称为显示锁,需要显示的手动获取释放锁,可以设置是否为公平锁。
Lock与synchronized一样可以实现线程同步,但比synchronized更强大,粒度更小,更灵活。
-
lock()
获取锁。 -
unlock()
释放锁。
ReentrantLock
显示锁的基本使用。
Lock#lock()
public class Demo {
public static void main(String[] args) {
Task task = new Task();
new Thread(task).start();
new Thread(task).start();
}
}
class Task implements Runnable {
private Lock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();
try {
Thread.sleep(3000L);
System.out.println(Thread.currentThread().getName() + " hello world");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Lock#tryLock()
非阻塞式获取锁。
-
lock()
是阻塞锁。 -
tryLock()
是非阻塞锁,当锁被占用时,可以执行其他任务;tryLock()
支持超时时间,在指定时间内可以获取锁返回true,否则返回false。
public class Demo {
public static void main(String[] args) {
Task task = new Task();
new Thread(task).start();
new Thread(task).start();
}
}
class Task implements Runnable {
private Lock lock = new ReentrantLock();
@Override
public void run() {
if (lock.tryLock()) {
try {
Thread.sleep(3000L);
System.out.println(Thread.currentThread().getName() + ": hello world");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} else {
System.out.println(Thread.currentThread().getName() + ": 锁被占用,执行其他任务!");
}
}
}
Lock#lockInterruptibly()
不可中断等待锁的线程:
public class Demo {
public static void main(String[] args) {
Task task = new Task();
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
//中断线程
t2.interrupt();
}
}
class Task implements Runnable {
private Lock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();
try {
Thread.sleep(3000L);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
输出:
java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at com.example.lib_java.Task.run(Demo.java:35)
at java.base/java.lang.Thread.run(Thread.java:834)
Thread-0
可中断等待锁的线程:
public class Demo {
public static void main(String[] args) {
Task task = new Task();
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
//中断线程
t2.interrupt();
}
}
class Task implements Runnable {
private Lock lock = new ReentrantLock();
@Override
public void run() {
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "被中断");
return;
}
try {
Thread.sleep(3000L);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
输出:
Thread-1被中断
Thread-0
await() & signal() & signalAll()
Condition的用法基本与synchronized的等待唤醒基本一致。
synchronized | Condition |
---|---|
wait() | await() |
wait(long timeout) | await(long time, TimeUnit unit) |
notify() | signal() |
notifyAll() | signalAll() |
await() 使用:
public class Demo {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Task task = new Task(lock, condition);
new Thread(task).start();
}
}
class Task implements Runnable {
private Lock lock;
private Condition condition;
public Task(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
lock.lock();
try {
//等待
condition.await();
System.out.println("hello world");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
说明:线程处于等待阶段,“hello world”一直没有输出。
await() & signal() 使用:
public class Demo {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Task task = new Task(lock, condition);
new Thread(task).start();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
//唤醒
condition.signal();
lock.unlock();
}
}
class Task implements Runnable {
private Lock lock;
private Condition condition;
public Task(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
lock.lock();
try {
//等待
condition.await();
System.out.println("hello world");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
说明:线程进入等待阶段,1秒后被唤醒,“hello world”被输出。
await() & signalAll() 使用:
public class Demo {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Task task = new Task(lock, condition);
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
//唤醒所有等待
condition.signalAll();
lock.unlock();
}
}
class Task implements Runnable {
private Lock lock;
private Condition condition;
public Task(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
lock.lock();
try {
condition.await();
System.out.println("hello world");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
说明:1秒后,三个线程都被唤醒,输出了3个“hello world”。
读写锁(ReadWriteLock)
- 读锁:也称为共享锁,线程共享的,用于读取操作的同步锁,锁对象是
ReentrantReadWriteLock.ReadLock
。 - 写锁:也称为独占锁,线程独占的,用于读取操作的同步锁,锁对象是
ReentrantReadWriteLock.WriterLock
。
论证读锁是共享锁
public class Demo {
public static void main(String[] args) {
Task task = new Task();
Thread t1 = new Thread(task, "线程一");
Thread t2 = new Thread(task, "线程二");
Thread t3 = new Thread(task, "线程三");
t1.start();
t2.start();
t3.start();
}
}
class Task implements Runnable {
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
private Lock readLock = readWriteLock.readLock();
@Override
public void run() {
readLock.lock();
try {
Thread.sleep(3000L);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readLock.unlock();
}
}
}
说明:同时输出三条内容,说明三个线程同时拿到锁,所以读锁是共享的。
论证写锁是独占的
public class Demo {
public static void main(String[] args) {
Task task = new Task();
Thread t1 = new Thread(task, "线程一");
Thread t2 = new Thread(task, "线程二");
Thread t3 = new Thread(task, "线程三");
t1.start();
t2.start();
t3.start();
}
}
class Task implements Runnable {
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
private Lock writeLock = readWriteLock.writeLock();
@Override
public void run() {
writeLock.lock();
try {
Thread.sleep(3000L);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.unlock();
}
}
}
说明:依次输出三个线程的信息,说明三个线程是依次获取锁,所以写锁是独占的。
使用读写锁实现高并发容器
public class Demo {
public static void main(String[] args) {
//数据存储容器
RWDictionary dictionary = new RWDictionary();
//写任务
WriteTask writeTask = new WriteTask(dictionary);
//读任务
ReadTask readTask = new ReadTask(dictionary);
Thread write1 = new Thread(writeTask, "写入线程一");
Thread write2 = new Thread(writeTask, "写入线程二");
Thread write3 = new Thread(writeTask, "写入线程三");
Thread read1 = new Thread(readTask, "读取线程一");
Thread read2 = new Thread(readTask, "读取线程二");
Thread read3 = new Thread(readTask, "读取线程三");
write1.start();
write2.start();
write3.start();
read1.start();
read2.start();
read3.start();
}
}
/**
* 写任务
*/
class WriteTask implements Runnable {
private RWDictionary dictionary;
public WriteTask(RWDictionary dictionary) {
this.dictionary = dictionary;
}
@Override
public void run() {
int i = 0;
while (true) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
String name = Thread.currentThread().getName();
dictionary.put("hello", name + "---" + i++);
}
}
}
/**
* 读任务
*/
class ReadTask implements Runnable {
private RWDictionary dictionary;
public ReadTask(RWDictionary dictionary) {
this.dictionary = dictionary;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
String[] keys = dictionary.allKeys();
for (String key : keys) {
Object value = dictionary.get(key);
System.out.println(key + ":" + value);
}
}
}
}
/**
* 数据管理容器
*/
class RWDictionary {
//存储数据
private final HashMap<String, Object> map = new HashMap<>();
//读写锁
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
//读锁
private final Lock r = readWriteLock.readLock();
//写锁
private final Lock w = readWriteLock.writeLock();
/**
* 获取数据
*/
public Object get(String key) {
r.lock();
try {
return map.get(key);
} finally {
r.unlock();
}
}
/**
* 获取所有键
*/
public String[] allKeys() {
r.lock();
try {
return map.keySet().toArray(new String[0]);
} finally {
r.unlock();
}
}
/**
* 写入数据
*/
public Object put(String key, Object value) {
w.lock();
try {
return map.put(key, value);
} finally {
w.unlock();
}
}
/**
* 清理所有数据
*/
public void clear() {
w.lock();
try {
map.clear();
} finally {
w.unlock();
}
}
}
输出:
hello:写入线程一---193
hello:写入线程一---193
hello:写入线程一---193
hello:写入线程二---194
hello:写入线程二---194
hello:写入线程二---194
hello:写入线程一---195
hello:写入线程一---195
hello:写入线程一---195
hello:写入线程一---196
hello:写入线程一---196
hello:写入线程一---196