theme: healer-readable
highlight: a11y-dark
哈喽,各位小伙伴们,你们好呀,我是喵手。
今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。
我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!
前言
多线程并发编程是当今软件开发中的重要话题,Java作为广泛使用的编程语言之一,提供了丰富的线程同步机制。掌握好Java的同步机制可以提高程序的性能和可靠性。
本篇文章将介绍Java中的同步机制及其应用场景、优缺点分析等内容,帮助读者更好地理解并正确应用Java的同步机制。
摘要
Java的同步机制可分为以下几种:
- synchronized关键字
- ReentrantLock类
- Semaphore类
- CountDownLatch类
- CyclicBarrier类
本文将对以上同步机制进行详细介绍,并对其特点、应用场景、优缺点等进行分析。
Java之同步
简介
Java中的同步机制是为了解决多线程并发执行时可能会导致的线程安全问题而提出的。线程安全问题是指,当多个线程同时访问某些共享数据时,会产生不正确的结果或异常。
Java提供了多种同步机制来解决线程安全问题,其中最常见的是synchronized关键字和ReentrantLock类。此外,Semaphore、CountDownLatch和CyclicBarrier等类也可以实现同步机制。
源代码解析
synchronized关键字
synchronized关键字是Java中最常用的同步机制,它可以保证同一时间只有一个线程访问被synchronized关键字修饰的代码块或方法。
synchronized关键字可以修饰方法、代码块和类三个级别。修饰方法时,同步作用于整个方法;修饰代码块时,同步作用于被synchronized关键字包裹的代码块;修饰类时,同步作用于该类的所有对象。
下面是基于synchronized关键字实现的线程安全的单例模式:
代码语言:java复制package com.example.javase.se.classes.concurrency;
/**
* @Author ms
* @Date 2023-11-05 21:27
*/
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
如上代码是一个单例模式的实现,保证了在整个应用程序的运行过程中只存在一个Singleton实例。具体分析如下:
- 私有化构造方法,禁止外界创建对象,保证只能通过getInstance()方法获取实例。
- instance是一个静态变量,表示Singleton实例,初始值为null。
- getInstance()方法是获取Singleton实例的唯一入口,通过synchronized关键字保证线程安全。
- 当instance为null时,通过new Singleton()创建一个Singleton实例,实现懒加载(即在真正需要使用时才去创建对象)。
- 返回Singleton实例。
总体上,这个单例模式的实现可以保证线程安全和懒加载,但是在高并发环境下,每次调用getInstance()方法都会加锁,影响性能。可以通过双重检查锁定或静态内部类实现更好的性能和线程安全。
ReentrantLock类
ReentrantLock是Java提供的一个可重入锁,与synchronized关键字相比,它提供了更多的灵活性和功能。在使用时,需要通过lock()方法获取锁,通过unlock()方法释放锁。
代码语言:java复制package com.example.javase.se.classes.concurrency;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author ms
* @Date 2023-11-05 21:29
*/
public class MyRunnable implements Runnable {
private ReentrantLock lock = new ReentrantLock();
public void run() {
try {
lock.lock();
//临界区代码
} finally {
lock.unlock();
}
}
}
上述代码中,通过lock()方法获取锁,通过unlock()方法释放锁。ReentrantLock类提供了更多的功能,例如公平锁和可中断锁等。
该代码是一个实现了Runnable接口的类,其中使用了ReentrantLock来实现对临界区的互斥访问。
在run()方法中,通过lock()方法获取锁,进入临界区进行操作。操作完毕后,通过unlock()方法释放锁,让其他线程可以获取锁进入临界区。
使用ReentrantLock可以保证线程安全,避免了多线程访问同一资源时可能出现的数据竞争问题。同时,在使用ReentrantLock时需要注意避免死锁的发生。
Semaphore类
Semaphore是Java提供的一个信号量类,可以用来限制同时访问某些资源的线程数量。Semaphore维护了一个计数器,每当一个线程访问资源时,计数器减一;当计数器为0时,其他线程将无法获取访问权限。
代码语言:java复制package com.example.javase.se.classes.concurrency;
import java.util.concurrent.Semaphore;
/**
* @Author ms
* @Date 2023-11-05 21:29
*/
public class MyRunnable implements Runnable {
private Semaphore semaphore = new Semaphore(5);
public void run() {
try {
semaphore.acquire();
//临界区代码
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
}
上述代码中,Semaphore初始化了一个计数器为5的实例,每当一个线程执行acquire()方法时,计数器减一;当计数器为0时,其他线程无法获取访问权限。当线程处理完资源后,需要调用release()方法来释放资源。
CountDownLatch类
CountDownLatch是Java提供的一个倒计时器类,可以用来实现线程的等待功能。CountDownLatch维护了一个计数器,当计数器为0时,等待线程将会被唤醒。
代码语言:java复制package com.example.javase.se.classes.concurrency;
import java.util.concurrent.CountDownLatch;
/**
* @Author ms
* @Date 2023-11-05 21:29
*/
public class MyRunnable implements Runnable {
private CountDownLatch latch;
public MyRunnable(CountDownLatch latch) {
this.latch = latch;
}
public void run() {
//临界区代码
latch.countDown();
}
}
上述代码中,当线程处理完资源后,需要调用countDown()方法来减少计数器的值。在等待线程中,需要调用await()方法来等待计数器为0。
CyclicBarrier类
CyclicBarrier是Java提供的一个屏障类,可以用来让多个线程在同一个点上进行等待。当所有线程都到达屏障点时,屏障将会打开,并且所有线程同时执行。
代码语言:java复制package com.example.javase.se.classes.concurrency;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @Author ms
* @Date 2023-11-05 21:29
*/
public class MyRunnable implements Runnable {
private CyclicBarrier barrier;
public MyRunnable(CyclicBarrier barrier) {
this.barrier = barrier;
}
public void run() {
//临界区代码
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
上述代码中,当线程到达屏障点时,需要调用await()方法来等待其他线程的到来。当所有线程都到达屏障点时,屏障将会打开,并且所有线程同时执行。
应用场景案例
synchronized关键字
synchronized关键字适用于多个线程访问共享资源的情况,例如单例模式、线程安全的集合、读写锁等。
ReentrantLock类
ReentrantLock类适用于需要更多灵活性和功能的情况,例如公平锁、非阻塞锁等。
Semaphore类
Semaphore类适用于限制同时访问某些资源的线程数量的情况,例如连接池、并发访问文件等。
CountDownLatch类
CountDownLatch类适用于需要等待特定条件的情况,例如线程池任务等待、分布式系统初始化等。
CyclicBarrier类
CyclicBarrier类适用于需要让多个线程在某个点上进行同步等待的情况,例如多线程计算结果合并、多线程数据处理等。
优缺点分析
synchronized关键字
优点:
- 简单易用,不需要额外的代码。
- 可以作用于方法、代码块和类三个级别。
缺点:
- 性能较差,会造成线程竞争和等待,导致效率低下。
- 无法中断一个正在等待获得锁的线程。
ReentrantLock类
优点:
- 与synchronized关键字相比,提供了更多的灵活性和功能。
- 可以设置公平锁和非阻塞锁等。
缺点:
- 使用复杂,需要手动获取锁和释放锁。
- 可能会产生死锁或竞争条件等问题。
Semaphore类
优点:
- 可以限制同时访问某些资源的线程数量。
- 可以进行多个资源的同时访问。
缺点:
- 使用复杂,需要手动获取和释放信号量。
- 无法中断一个正在等待获取信号量的线程。
CountDownLatch类
优点:
- 可以让线程等待特定的条件。
- 可以实现多个线程等待某个操作的完成。
缺点:
- 不能重用,一旦计数器为0,就无法再次使用。
- 无法中断一个正在等待计数器的线程。
CyclicBarrier类
优点:
- 可以让多个线程在某个点上同步等待。
缺点:
- 不能重用,一旦屏障打开,就无法再次使用。
- 所有线程必须同时到达屏障点,否则会一直等待。
小结
Java提供了多种同步机制来解决并发编程中可能出现的线程安全问题,不同的同步机制有不同的特点和适用场景。在实际应用中,需要根据具体情况来选择合适的同步机制,并注意其优缺点,以达到最优的性能和可靠性。同时,还需要注意避免死锁、竞争条件等问题。
总结
本文介绍了Java中常用的同步机制,包括synchronized关键字、ReentrantLock类、Semaphore类、CountDownLatch类和CyclicBarrier类。对于每种机制,文章都详细介绍了其使用方法、优缺点和适用场景。在实际应用中,需要根据具体情况选择合适的同步机制,并注意避免可能出现的死锁和竞争条件等问题。同时,文章也强调了学习的重要性,提出了学习需要虚心求教的观点。
... ...
文末
好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。
... ...
学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!
wished for you successed !!!
⭐️若喜欢我,就请关注我叭。
⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!