20240619-James-快速鸟瞰并发编程, 呕心沥血整理的架构技术(第1篇)

享学课堂James 2019/06/19 15:27

接着第1篇后,我们继续来跟进一下并发编程的其它内容,如下:

Java程序员,你必须得知道并发编程概念

大家好,我是享学课堂风骚走位的James, 并发编程做为Java编程的核心灵魂, 不管在面试还是在工作中,都是非常重要的, 花了不少时间我整理出了并发编程的一个核心知识, 希望能够帮助更多的Java开发人员,在工作中合理使用多线程, 让你的代码更高效, 更直观。大概分为以下板块。

目录

► 简介

► 概念

►Java内存模型:Happens-before 关系

► 标准同步功能

► 安全发布

► 对象不可变

► 线程Thread类

► 线程的活跃度: 死锁与活锁

► java.util.concurrent包

第1节 简介

所谓并发编程是指在一台处理器上“同时”处理多个任务。并发是在同一实体上的多个事件。这段是从百度百科找到的解释, 我而的解释, 你所写的任何一行代码, 它的执行都是用线程来运行的, 也就是说并发编程从直接决定了你系统性能运行情况, 能让你的系统起飞, 也能让你的系统性能变成蜗牛。

第2节 概念

从JDK的最低版本开始,Java就支持了线程和锁等关键并发概念。那么这些概念务必记到你脑海深处,哈哈.

概念描述
原子操作原子操作是指不会被线程调度机制打断的操作( 其实就是你的业务操作一开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)
可见性一个线程对共享变量值的修改,能够及时的被其他线程看到。

前提条件

当多个线程对共享资源执行一系列操作时, 它们会对竞争共享资源, 每个线程的操作顺序不一样, 会导致多个不可预期的操作结果。比如以下的代码就为非线程安全的,其中value可以多次初始化,因为if (value == null)做了null 判断, 然后初始化的, 延迟初始化的字段不是原子的

class JamesLazy {
private volatile T value;
T get() {
if (value == null)//这里做了null判断, 延迟初始化的字段它不是原子的
value = initialize();
return value;
}
}

数据竞争

当两个或多个线程在没有同步的情况下尝试访问相同的非final变量时,就会发生数据竞争。不使用同步可能导致你的所有操作对而其它线程是不可见的,因此可以读取过时数据,但若反过来可能会产生无限循环,损坏的数据结构或不准确的计算等后果。此代码可能会导致无限循环,因为读者线程可能永远不会观察到写入器线程所做的更改:

class JamesWaiter implements Runnable {
private boolean shouldFinish;
void finish() {
shouldFinish = true;
}

public void run() {
long iteration = 0;
while (!shouldFinish) {
iteration++;
}
System.out.println("完成后的结果: " + iteration);
}
}

class JamesDataRace {
public static void main(String[] args) throws InterruptedException {
JamesWaiter waiter = new JamesWaiter();
Thread waiterThread = new Thread(waiter);
waiterThread.start();
waiter.finish();
waiterThread.join();
}
}

运行结果为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第3节 Java内存模型:Happens-before 关系

Java内存模型是根据读取和写入字段以及在监听器上同步等操作定义的。可以通过Happens-before关系对操作进行排序,一般用于推断线程何时看到另一个线程的操作结果,以及用来分析同步的程序构成状况。

Happens-before关系具有以下特性:
在Thread1线程执行任何业务操作之前调用Thread1.start()。也就是说Thread1对象的start()方法先行发生于此线程的任何一个操作
在监视器锁上的解锁操作必须在同一个监视器锁上的加锁操作之前执行。
对volatile变量的写入在任何后续读取volatile变量之前发生。
在发布对象的引用之前发生对最终变量的写入。
线程中的所有操作都是在从Thread#join该线程返回之前发生的。

在下图中,Thread 1的Action X操作在Action Y操作之前就调用了,因此在Thread 2中所有操作Action Y的右侧业务操作时, 将会看到Thread 1中Action X前的所有操作。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第4节 标准同步功能

synchronized关键字

synchronized关键字用于防止不同的线程同时执行相同的代码块, 其实就是指这个代码块只被一个线程执行。当线程A获得synchronized锁后,只有线程A访问synchronized代码块,是一种独占式模式操作, 例如: 13号技师被王根基同学带进屋后,王根基同学在门上挂了把锁, 其它线程得等待, 保证了13号技师只能与王根基(线程)进行业务操作 ,不难看出这个操作原子操作(只有王根基线程操作13号技师)。此外,它保证其它线程在获取相同的锁之后将观察正在操作线程的结果,何时释放锁。

class JamesAtomicOperation {
private int counter0 ;
private int counter1 ;
void increment(){
synchronized(this){
counter0 ++ ;
counter1 ++ ;
}
}
}

synchronized关键字可以在方法级来指定。

方法类型描述
静态对该类对象加锁,俗称“类锁”
非静态参考以上synchronized(this)代码块,对调用该方法的对象加锁,俗称“对象锁”**

锁是可重入的,因此如果线程已经拥有锁,可以再次成功获取它。

class JamesReentrantcy {
synchronized void doAll(){
doFirst();
doSecond();
}
synchronized void doFirst(){
System.out.println(“第一次操作成功。”);
}
synchronized void doSecond(){
System.out.println(“第二次操作成功。”);
}
}

等待/通知

wait/notify/notifyAll方法在Object类中声明。wait的作用可以使线程状态变成WAITINGTIMED_WAITING(如果已等待超时)状态。为了唤醒一个线程,可以执行以下任何操作:

  • 另一个线程调用notify,唤醒在监视器上等待的任意线程。
  • 另一个线程调用notifyAll,唤醒监视器上等待的所有线程。
  • 如果调用 Thread#interrupt。在这种情况下,会抛出InterruptedException

最常见的模式是条件循环:

class JamesConditionLoop {
private boolean condition;
synchronized void waitForCondition()throws InterruptedException {
while(!condition){
wait();
}
}
synchronized void satisfCondition(){
condition = true ;
notifyAll();
}
}

  • 同志们请记住,如果wait/notify/notifyAll要在你的对象上使用,需要首先获取此对象锁。
  • 总是在一个循环中等待检查正在等待的条件 - 如果另一个线程在等待开始之前满足条件,这就解决了时间问题。此外,它还可以保护您的代码免受可能(并且确实发生)的虚假唤醒。
  • 在打电话之前,请务必确保您满足等待条件notify/notifyAll。如果不这样做将导致通知,但没有线程能够逃脱其等待循环。

volatile关键字

volatile解决了多线程之间的资源可见性问题,有这么一层关系大家需要知道:对某个volatile字段的写操作happens-before后续对同一个volatile字段的读操作,比如线程1写入了volatile变量v(这里和后续的“变量”都指的是对象的字段、类字段和数组元素),接着线程2读取了v,那么,线程1写入v及之前的写操作都对线程2可见(线程1和线程2可以是同一个线程)。因此,它保证字段的任何后续读取都将看到由最近写入设置的值。

class JamesVolatileFlag implements Runnable {
private volatile boolean shouldStop;

public void run() {
while (!shouldStop) {
// TODO业务代码
}
System.out.println(“停止.”);
}

void stop() {
shouldStop = true;
}

public static void main(String[] args) throws InterruptedException {
JamesVolatileFlag flag = new JamesVolatileFlag();
Thread thread = new Thread(flag);
thread.start();

flag.stop();
thread.join();
}
}

原子操作

java.util.concurrent.atomic包路径下的一些类以无锁方式支持单个值上的原子复合操作,类似于volatile。

使用AtomicXXX类,可以实现原子check-then-act操作:

class JamesCheckThenAct {
private final AtomicReference value = new AtomicReference<>();

原子操作

java.util.concurrent.atomic包路径下的一些类以无锁方式支持单个值上的原子复合操作,类似于volatile。

使用AtomicXXX类,可以实现原子check-then-act操作:

class JamesCheckThenAct {
private final AtomicReference value = new AtomicReference<>();

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
森林防火应急联动指挥系统是一个集成了北斗定位/GPS、GIS、RS遥感、无线网络通讯、4G网络等技术的现代化智能系统,旨在提高森林火灾的预防和扑救效率。该系统通过实时监控、地图服务、历史数据管理、调度语音等功能,实现了现场指挥调度、语音呼叫通讯、远程监控、现场直播、救火人员生命检测等工作的网络化、智能化、可视化。它能够在火灾发生后迅速组网,确保现场与指挥中心的通信畅通,同时,系统支持快速部署,适应各种极端环境,保障信息的实时传输和历史数据的安全存储。 系统的设计遵循先进性、实用性、标准性、开放性、安全性、可靠性和扩展性原则,确保了技术的领先地位和未来的发展空间。系统架构包括应急终端、无线专网、应用联动应用和服务组件,以及安全审计模块,以确保用户合法性和数据安全性。部署方案灵活,能够根据现场需求快速搭建应急指挥平台,支持高并发视频直播和大容量数据存储。 智能终端设备具备三防等级,能够在恶劣环境下稳定工作,支持北斗+GPS双模定位,提供精确的位置信息。设备搭载的操作系统和处理器能够处理复杂的任务,如高清视频拍摄和数据传输。此外,设备还配备了多种传感器和接口,以适应不同的使用场景。 自适应无线网络是系统的关键组成部分,它基于认知无线电技术,能够根据环境变化动态调整通讯参数,优化通讯效果。网络支持点对点和点对多点的组网模式,具有低功耗、长距离覆盖、强抗干扰能力等特点,易于部署和维护。 系统的售后服务保障包括安装实施服务、系统维护服务、系统完善服务、培训服务等,确保用户能够高效使用系统。提供7*24小时的实时故障响应,以及定期的系统优化和维护,确保系统的稳定运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值