synchronized和lock
synchronized
synchronized关键字用于Java中同步方法或同步代码块中,防止资源冲突。当任务要执行被synchronized关键字保护的代码片段的时候,它将检查锁是否可用,然后获取锁,执行代码,释放锁。
基本使用
同步方法
public synchronized void synchronizedMethod() {
// 该方法的内容在同一时间只能被一个线程执行
// ...
}
同步代码块
public void someMethod() {
// 非同步的代码
synchronized (this) {
// 被同步的代码块
// ...
}
// 非同步的代码
}
其余不在赘述,相关使用应该很常见
lock
ReentrantLock
除了使用synchronized外,我们可以使用Lock接口写的ReentrantLock
实现独占锁的功能。
查看类图可知,ReentrantLock
实现了Lock接口
Lock接口定义了几个方法用来实现锁的基本功能
lock() : void
//获得锁。lockInterruptibly() : void
// 获取锁定,除非当前线程是 interrupted 。tryLock() : boolean
// 只有在调用时才可以获得锁tryLock(time : long,unit : TimeUnit ) : boolean
// 如果在给定的等待时间内是空闲的,并且当前的线程尚未得到 interrupted,则获取该锁。unlock() : void
// 释放锁。newCondition() : Condition
// 返回一个新Condition
绑定到该实例Lock
实例。
常用方法应该是lock()/unlock()和newCondition(),简要阐述一下,由ReentrantLock实现
lock和unlock
lock和unlock方法是Lock接口提供的重要的加锁解锁方法,他们包围的代码片段就是相应的加锁处理片段,下面是一个简单的示例
public class LockExample {
private Lock lock = new ReentrantLock();
private Object object = new Object();
public void test(String[] args) {
lock.lock();
System.out.println("lock 测试"+ object.toString());
lock.unlock();
}
}
只需要新建Lock对象后在需要锁的开始和结束处使用lock方法和unlock方法进行加锁和解锁就可以完成相应的功能。
需要注意,如果锁内部代码出现异常可能会造成lock对象的unlock方法没有执行,从而未释放锁。所以相应的功能应该卸载try catch代码块中
public class LockExample {
private Lock lock = new ReentrantLock();
private Object object = new Object();
public void test(String[] args) {
lock.lock();
try{
System.out.println("lock 测试"+ object.toString());
}catch (Exception e){
e.printStackTrace();
}
finally {
lock.unlock();
}
}
}
newCondition
newCondition
()方法是Lock接口中线程通讯的重要实现,他返回一个Condition
对象,Condition
类似于Object
监视器方法( wait
, notify
和notifyAll
),将其中的重要方法封装成了不同的对象,以得到具有多个等待集的每个对象,通过将它们与使用任意的组合的效果Lock
个实现。 Lock
替换synchronized
方法和语句的使用, Condition
取代了对象监视器方法的使用。 下面是一个简单的示例
package com.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
// 定义一个共享的计数器
private int count = 0;
// 定义一个锁和条件变量
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
// 定义一个增加计数器的方法
public void increment() {
lock.lock();
try {
count++;
System.out.println("Counter value: " + count);
// 通知其他线程,计数器已经增加
condition.signal();
} finally {
lock.unlock();
}
}
// 定义一个减少计数器的方法
public void decrement() {
lock.lock();
try {
while (count == 0) {
// 等待其他线程通知,计数器已经增加
condition.await();
}
count--;
System.out.println("Counter value: " + count);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
Counter counter = new Counter();
// 创建两个线程,一个增加计数器,一个减少计数器
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
counter.decrement();
}
});
// 启动线程
t1.start();
t2.start();
// 等待线程结束
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
该类定义了一个资源类和两个方法,一个增加计数,一个减少计数,循环5次,后通知另一线程执行,结果如下:
Counter value: 1
Counter value: 2
Counter value: 3
Counter value: 4
Counter value: 5
Counter value: 4
Counter value: 3
Counter value: 2
Counter value: 1
Counter value: 0
condition的使用类似于Object方法中的wait和notify方法,不同的是Condition类可以提供多个条件,实现更细粒度的线程等待和通知
package com.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MultiConditionExample {
private final Lock lock = new ReentrantLock();
private final Condition condition1 = lock.newCondition();
private final Condition condition2 = lock.newCondition();
private boolean condition1Met = false;
private boolean condition2Met = false;
public void awaitCondition1() throws InterruptedException {
lock.lock();
try {
while (!condition1Met) {
condition1.await();
}
// condition1Met 为 true,执行相关操作
System.out.println("Condition 1 开始运行");
} finally {
lock.unlock();
}
}
public void awaitCondition2() throws InterruptedException {
lock.lock();
try {
while (!condition2Met) {
condition2.await();
}
// condition2Met 为 true,执行相关操作
System.out.println("Condition 2 met");
} finally {
lock.unlock();
}
}
public void signalCondition1() {
lock.lock();
try {
condition1Met = true;
condition1.signal();
} finally {
lock.unlock();
}
}
public void signalCondition2() {
lock.lock();
try {
condition2Met = true;
condition2.signal();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
MultiConditionExample example = new MultiConditionExample();
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(2000);
example.signalCondition1();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
try {
Thread.sleep(3000);
example.signalCondition2();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread3 = new Thread(() -> {
try {
example.awaitCondition1();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread4 = new Thread(() -> {
try {
example.awaitCondition2();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
thread3.start();
thread4.start();
try {
thread1.join();
thread2.join();
thread3.join();
thread4.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果
Condition 1 met
Condition 2 met
这个示例只是将一对Condition信号复制了一份放在一个类中执行,不同于wait/notify,Condition在这个可以根据实例信号(condition1/condition2)来指定需要通讯的线程。
Synchronized和Lock对比
- 获取方式:
Synchronized
:通过synchronized
关键字来实现,可以修饰方法或代码块。Lock
:通过Lock
接口的实现类(如ReentrantLock
)来创建实例,并调用其方法进行获取锁。
- 使用方式:
Synchronized
:在方法上使用synchronized
关键字或使用synchronized
块来指定需要同步的代码块。Lock
:使用Lock
对象的lock()
方法获取锁,然后使用unlock()
方法释放锁。
- 灵活性:
Synchronized
:是内置的、隐式的锁机制,具有自动释放锁的特性,可以很方便地使用,但相对较为简单,提供的功能相对有限。Lock
:是显式的锁机制,提供了更多的灵活性和功能,例如可重入性、公平性、条件变量等,但需要手动管理锁的获取和释放。
- 性能:
Synchronized
:在 Java 6 之后进行了很多优化,性能已经大幅提升,尤其在无竞争的情况下,性能表现良好。Lock
:性能相对较好,并且在高度竞争的情况下能够提供更好的性能表现。
- 可中断性:
Synchronized
:一旦获取到锁,无法被其他线程中断,只能等待锁的释放。Lock
:提供了可中断的获取锁的方法lockInterruptibly()
,可以在等待锁的过程中响应中断。
- 条件变量:
Synchronized
:不直接支持条件变量,但可以通过Object
的wait()
、notify()
和notifyAll()
方法结合synchronized
关键字来实现类似的功能。Lock
:提供了Condition
接口来支持条件变量,可以使用newCondition()
方法创建条件对象,并使用其await()
、signal()
和signalAll()
方法进行线程等待和通知。
参考:
JDK官方文档:Synchronized Methods (The Java™ Tutorials > Essential Java Classes > Concurrency) (oracle.com)
synchronized VS Lock, wait-notify VS Condition
chatGPT3.5