全文目录:
-
- 开篇语
-
- 前言
-
- [35.1 同步代码块和同步方法](#35.1 同步代码块和同步方法)
-
- [35.1.1 同步方法](#35.1.1 同步方法)
- [35.1.2 同步代码块](#35.1.2 同步代码块)
- [35.2 Lock 接口和 ReentrantLock](#35.2 Lock 接口和 ReentrantLock)
-
- [35.2.1 Lock 接口](#35.2.1 Lock 接口)
- [35.2.2 ReentrantLock](#35.2.2 ReentrantLock)
- [35.3 读写锁(ReadWriteLock)](#35.3 读写锁(ReadWriteLock))
-
- [35.3.1 读写锁的工作原理](#35.3.1 读写锁的工作原理)
- [35.4 条件变量(Condition)](#35.4 条件变量(Condition))
-
- [35.4.1 使用 `Condition` 的基本步骤](#35.4.1 使用
Condition的基本步骤)
- [35.4.1 使用 `Condition` 的基本步骤](#35.4.1 使用
- [35.5 volatile 关键字](#35.5 volatile 关键字)
-
- [35.5.1 `volatile` 的使用场景](#35.5.1
volatile的使用场景)
- [35.5.1 `volatile` 的使用场景](#35.5.1
- 小结
- 文末
开篇语
今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。
我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!
前言
在并发编程中,多个线程同时访问共享资源时,可能会导致数据不一致或线程安全问题。线程同步 (Thread Synchronization)是一种机制,确保在多线程环境下,多个线程对共享资源的访问是安全的。Java 提供了多种同步方式来保证线程之间的互斥和协作。今天,我们将深入探讨同步代码块、同步方法、Lock 接口、ReentrantLock、读写锁、条件变量以及 volatile 关键字。
35.1 同步代码块和同步方法
在 Java 中,可以通过 同步代码块 或 同步方法 来确保对共享资源的访问是线程安全的。
35.1.1 同步方法
同步方法是通过 synchronized 关键字修饰的方法,它确保在同一时刻只能有一个线程执行该方法。这种方式是通过 锁定 当前对象来实现的。对于静态方法,锁定的是 类的字节码对象。
java
class Counter {
private int count = 0;
// 同步方法
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class Main {
public static void main(String[] args) {
Counter counter = new Counter();
// 创建多个线程操作同一个对象
Thread t1 = new Thread(() -> counter.increment());
Thread t2 = new Thread(() -> counter.increment());
t1.start();
t2.start();
}
}
在上面的例子中,increment() 方法是同步的,确保了多个线程不会同时修改 count,从而避免了线程安全问题。
35.1.2 同步代码块
同步代码块是通过 synchronized 关键字来修饰某个特定的代码块。它允许我们在方法中对某一部分代码进行同步,而不是整个方法。同步代码块通常用于提高程序的并发性能,因为它可以减少锁的粒度。
java
class Counter {
private int count = 0;
public void increment() {
synchronized (this) { // 同步代码块
count++;
}
}
public int getCount() {
return count;
}
}
同步代码块允许我们选择性地同步某些部分,而不是整个方法,从而减少锁的竞争,提高程序的执行效率。
35.2 Lock 接口和 ReentrantLock
在 Java 中,synchronized 是一种内置的同步机制,但它存在一些限制,比如无法尝试获取锁、没有超时机制等。为了解决这些问题,Java 引入了 Lock 接口和 ReentrantLock 类。
35.2.1 Lock 接口
Lock 接口提供了一些比 synchronized 更灵活的锁机制。与 synchronized 相比,Lock 可以更加精确地控制锁的获取和释放。
- lock():获取锁。
- unlock():释放锁。
- tryLock() :尝试获取锁,如果锁不可用,则返回
false。 - lockInterruptibly():可以响应中断的锁获取方法。
35.2.2 ReentrantLock
ReentrantLock 是 Lock 接口的一个实现类,支持可重入锁,即同一个线程可以多次获取该锁,避免了死锁的发生。它还支持公平锁和非公平锁。
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
private Lock lock = new ReentrantLock(); // 使用ReentrantLock
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 确保释放锁
}
}
public int getCount() {
return count;
}
}
public class Main {
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(() -> counter.increment());
Thread t2 = new Thread(() -> counter.increment());
t1.start();
t2.start();
}
}
在上面的代码中,ReentrantLock 提供了更加细粒度的控制,我们可以通过调用 lock() 来获取锁,unlock() 来释放锁,确保多线程环境中的数据一致性。
35.3 读写锁(ReadWriteLock)
在多线程环境中,如果有多个线程频繁地读取共享资源,而写操作较少,使用 读写锁 (ReadWriteLock)可以提高程序的并发性能。读写锁允许多个线程同时读取资源,但写操作是互斥的。Java 提供了 ReadWriteLock 接口及其实现类 ReentrantReadWriteLock。
35.3.1 读写锁的工作原理
- 读锁:多个线程可以同时获取读锁。
- 写锁:只有一个线程能获取写锁,且写锁期间没有任何线程可以获取读锁或写锁。
java
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class Data {
private int value = 0;
private ReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
lock.readLock().lock(); // 获取读锁
try {
System.out.println("Read value: " + value);
} finally {
lock.readLock().unlock(); // 释放读锁
}
}
public void write(int newValue) {
lock.writeLock().lock(); // 获取写锁
try {
value = newValue;
System.out.println("Written value: " + value);
} finally {
lock.writeLock().unlock(); // 释放写锁
}
}
}
public class Main {
public static void main(String[] args) {
Data data = new Data();
Thread t1 = new Thread(() -> data.read());
Thread t2 = new Thread(() -> data.write(42));
t1.start();
t2.start();
}
}
在上面的例子中,ReentrantReadWriteLock 实现了读写锁,使得多个线程可以并发地读取资源,而写入操作则是互斥的,从而提高了程序的并发性。
35.4 条件变量(Condition)
在多线程编程中,我们经常需要某些线程等待某些条件的发生。条件变量 (Condition)是通过 Lock 接口提供的机制,它允许线程在满足某个条件之前挂起,直到条件被其他线程满足并发出通知。
35.4.1 使用 Condition 的基本步骤
- 使用
Condition对象来实现等待和通知机制。 - 线程通过
await()方法等待条件的满足。 - 条件满足时,通过
signal()或signalAll()方法通知其他线程。
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
class SharedResource {
private int data = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void produce() {
lock.lock();
try {
while (data > 0) {
condition.await(); // 等待消费者消费
}
data++;
System.out.println("Produced data: " + data);
condition.signalAll(); // 通知消费者
} finally {
lock.unlock();
}
}
public void consume() {
lock.lock();
try {
while (data == 0) {
condition.await(); // 等待生产者生产
}
data--;
System.out.println("Consumed data: " + data);
condition.signalAll(); // 通知生产者
} finally {
lock.unlock();
}
}
}
public class Main {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
// 创建生产者线程
Thread producer = new Thread(() -> {
for (int i = 0; i < 5; i++) {
resource.produce();
}
});
// 创建消费者线程
Thread consumer = new Thread(() -> {
for (int i = 0; i < 5; i++) {
resource.consume();
}
});
producer.start();
consumer.start();
}
}
在上面的代码中,生产者和消费者使用 Condition 来协调工作,确保生产和消费按照特定顺序进行。
35.5 volatile 关键字
volatile 是 Java 中的一种轻量级的同步机制,它保证了变量的修改对其他线程可见。使用 volatile 修饰的变量不会被线程的本地缓存所缓存,每次读取该变量时都会直接从主内存中获取。
35.5.1 volatile 的使用场景
volatile 适用于简单的状态标志变量,确保线程间的可见性,但它并不提供原子性,因此不适用于需要复合操作的场景。
java
class SharedFlag {
private volatile boolean flag = false;
public void setFlagTrue() {
flag = true;
}
public boolean getFlag() {
return flag;
}
}
使用 volatile 可以确保变量的状态在多线程环境下正确同步,但如果需要保证复合操作的原子性,应该使用 synchronized 或 Lock。
小结
线程同步是确保多线程程序正确执行的关键。通过 同步代码块 和 同步方法 ,我们可以控制线程的执行顺序,避免竞争条件。Lock 接口和 ReentrantLock 提供了比 synchronized 更细粒度的锁控制,读写锁允许我们在高并发环境下提高程序的性能,Condition 为线程间的协作提供了更加灵活的等待和通知机制,而 volatile 提供了一种轻量级的线程间通信机制。掌握这些同步机制,将帮助我们编写高效、可靠的多线程程序。
... ...
文末
好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。
... ...
学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!
wished for you successed !!!
⭐️若喜欢我,就请关注我叭。
⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。
版权声明:本文由作者原创,转载请注明出处,谢谢支持!