synchronized和lock
synchronized
synchronized关键字用于Java中同步方法或同步代码块中,防止资源冲突。当任务要执行被synchronized关键字保护的代码片段的时候,它将检查锁是否可用,然后获取锁,执行代码,释放锁。
基本使用
同步方法
1 2 3 4
| public synchronized void synchronizedMethod() { }
|
同步代码块
1 2 3 4 5 6 7 8 9 10
| 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接口提供的重要的加锁解锁方法,他们包围的代码片段就是相应的加锁处理片段,下面是一个简单的示例
1 2 3 4 5 6 7 8 9 10 11 12
| 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代码块中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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
取代了对象监视器方法的使用。 下面是一个简单的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| 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次,后通知另一线程执行,结果如下:
1 2 3 4 5 6 7 8 9 10
| 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类可以提供多个条件,实现更细粒度的线程等待和通知
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| 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(); } System.out.println("Condition 1 开始运行"); } finally { lock.unlock(); } }
public void awaitCondition2() throws InterruptedException { lock.lock(); try { while (!condition2Met) { condition2.await(); } 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(); } } }
|
结果
1 2
| 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