培训网站项目ppt怎么做,抖音服务商,西咸新区规划建设局网站,湖州做网站的Java并发编程-理论基础
1、什么是进程#xff1f;
进程#xff08;Process#xff09;是计算机中的程序关于某数据集合上的一次运行活动#xff0c;是系统进行资源分配的基本单位#xff0c;是操作系统结构的基础。在早期面向进程设计的计算机结构中#xff0c;进程是程…Java并发编程-理论基础
1、什么是进程
进程Process是计算机中的程序关于某数据集合上的一次运行活动是系统进行资源分配的基本单位是操作系统结构的基础。在早期面向进程设计的计算机结构中进程是程序的基本执行实体在当代面向线程设计的计算机结构中进程是线程的容器。程序是指令、数据及其组织形式的描述进程是程序的实体。
2、什么是线程
线程英语thread是操作系统能够进行运算调度的最小单位。它被包含在进程之中是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流一个进程中可以并发多个线程每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程lightweight processes但轻量进程更多指内核线程kernel thread而把用户线程user thread称为线程。
线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程如Win32线程由用户进程自行调度的用户线程如Linux平台的POSIX Thread或者由内核与用户进程如Windows 7的线程进行混合调度。
同一进程中的多条线程将共享该进程中的全部系统资源如虚拟地址空间文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈call stack自己的寄存器环境register context自己的线程本地存储thread-local storage。
一个进程可以有很多线程每条线程并行执行不同的任务。
在多核或多CPU或支持Hyper-threading的CPU上使用多线程程序设计的好处是显而易见即提高了程序的执行吞吐率。在单CPU单核的计算机上使用多线程技术也可以把进程中负责I/O处理、人机交互而常被阻塞的部分与密集计算的部分分开来执行编写专门的workhorse线程执行密集计算从而提高了程序的执行效率。
进程与线程的对比 特性 进程 线程 定义 资源分配的基本单位 CPU调度的基本单位 内存空间 独立内存空间 共享进程内存空间 通信 需要IPC机制(管道、消息队列等) 可直接读写进程数据段 创建开销 大(需要分配独立资源) 小(共享已有资源) 稳定性 一个进程崩溃不影响其他进程 一个线程崩溃可能导致整个进程崩溃 并发性 进程间并发 线程间并发
3、为什么需要多线程
从CPU、内存、I/O速度差异的角度分析 计算机系统中CPU、内存、I/O设备的速度差异极大
CPU 计算速度极快纳秒级内存 访问速度较慢百纳秒级I/O设备磁盘、网络更慢毫秒级比CPU慢百万倍
为了合理利用CPU的高性能计算机体系结构、操作系统和编译器分别采用了优化手段但也引入了并发问题
1. CPU缓存Cache——解决CPU与内存速度差异
问题CPU计算速度远高于内存访问速度直接读写内存会导致CPU大部分时间等待内存墙问题。解决方案引入多级缓存L1/L2/L3 Cache减少CPU访问内存的次数。副作用可见性问题 多线程环境下一个线程修改了缓存数据另一个线程可能无法立即看到缓存一致性问题。需要内存屏障Memory Barrier或volatile关键字保证可见性。
总结缓存提高CPU利用率但导致多线程间数据不可见。
2. 进程/线程分时复用——解决CPU与I/O速度差异
问题I/O操作磁盘、网络极慢单线程程序会让CPU大部分时间等待I/O。解决方案 进程操作系统提供隔离的执行环境但切换开销大。线程轻量级执行单元共享进程资源切换成本低。多线程当一个线程等待I/O时CPU可以执行其他线程提高利用率。
副作用原子性问题 线程切换可能导致非原子操作被中断如i并非原子操作。需要锁synchronized或CASCompare-And-Swap保证原子性。
总结多线程提高CPU利用率但导致竞态条件Race Condition。
3. 编译器/CPU指令重排序——优化缓存利用率
问题为了减少CPU等待数据的时间编译器和CPU会优化指令执行顺序如乱序执行。解决方案 指令重排调整无关指令的顺序提高缓存命中率。
副作用有序性问题 多线程环境下指令重排可能导致逻辑错误如单例模式的双重检查锁失效。需要内存屏障Memory Barrier或volatile禁止重排序。
总结指令重排提高性能但导致多线程执行顺序不可预测。
4、线程不安全如何产生的Java中
如果多个线程对同一个共享数据进行访问而不采取同步操作的话那么操作的结果是不一致的。
银行账户取款问题
public class UnsafeBankAccount {private int balance; // 共享数据public UnsafeBankAccount(int initialBalance) {this.balance initialBalance;}// 不安全的取款方法public void withdraw(int amount) {if (amount balance) {// 模拟一些处理时间try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}balance - amount;System.out.println(Thread.currentThread().getName() 取款 amount 成功余额: balance);} else {System.out.println(Thread.currentThread().getName() 取款 amount 失败余额不足);}}public static void main(String[] args) {UnsafeBankAccount account new UnsafeBankAccount(1000);// 创建多个线程同时取款for (int i 0; i 10; i) {new Thread(() - account.withdraw(800)).start();}}
}
运行结果 问题分析
竞态条件(Race Condition) 多个线程同时检查余额 if (amount balance)在检查后但实际扣款前其他线程可能已经修改了余额导致多个线程都认为可以取款最终余额变为负数
操作非原子性
balance - amount 不是原子操作它实际上是 java复制下载
int temp balance;
temp temp - amount;
balance temp; 线程可能在中间步骤被中断
内存可见性问题 一个线程对balance的修改可能不会立即对其他线程可见导致线程看到的是过期的balance值
线程不安全的核心原因
共享数据的并发访问多个线程同时读写同一个变量操作的非原子性操作不是一次性完成的可能被中断缺乏同步机制没有使用synchronized、Lock等同步手段内存可见性问题线程可能看不到其他线程的最新修改
5、并发三要素
在并发编程中问题的根源可以归结为三个核心要素可见性、原子性、有序性
可见性visibility
可见性问题指的是一个线程对共享变量的修改另一个线程不能立即看到。这是由于现代计算机架构中CPU缓存与主内存之间的同步延迟导致的。
可见性问题源于现代计算机的多级存储架构
CPU缓存架构每个CPU核心有自己的缓存L1、L2多个核心共享L3缓存缓存一致性协议如MESI协议但存在延迟编译器优化可能将变量缓存在寄存器中而不是从内存读取
在Java内存模型(JMM)中每个线程有自己的工作内存可以理解为CPU缓存的抽象导致线程对共享变量的修改可能不会立即同步到主内存其他线程也就无法立即看到修改。
可见性完整案例
案例1体现可见性问题的服务控制器基础可见性问题
package com.taiyuan.javademoone.threaddemo.demo001;import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** 修改后的ServiceController强制体现可见性问题* 通过纯空循环和长时间运行来暴露可见性问题*/
public class ServiceController {// 无volatile修饰保证会出现可见性问题private static boolean isRunning true;//TODO 解决方案1、添加volatile修饰保证可见性// private static volatile boolean isRunning true;// TODO 解决方案2、AtomicBoolean保证可见性// private static AtomicBoolean isRunning new AtomicBoolean(true);// TODO 解决方案3、使用synchronized关键字保证可见性// public static synchronized boolean isRunning() {// return isRunning;// }// public static synchronized void stopRunning() {// isRunning false;// }// TODO 解决方案4、使用Lock保证可见性private static final Lock lock new ReentrantLock();public static boolean isRunning() {lock.lock();try {return isRunning;} finally {lock.unlock();}}public static void stopRunning() {lock.lock();try {isRunning false;} finally {lock.unlock();}}// 添加一个计数器用于验证循环执行次数private static long counter 0;public static void main(String[] args) throws InterruptedException {// 工作线程 - 使用纯空循环Thread worker new Thread(() - {while (isRunning) {counter; // 纯计数操作无任何同步点}System.out.println(工作线程停止循环次数: counter);});worker.start();// 主线程稍后停止服务Thread.sleep(3000);isRunning false;// stopRunning();System.out.println(主线程将isRunning设置为false);// 等待工作线程结束(最多10秒)worker.join(10000);if (worker.isAlive()) {System.out.println(警告工作线程未能正常停止);System.out.println(最后计数: counter);}}
} 解决可见性问题
方案1使用volatile推荐
private static volatile boolean isRunning true;
方案2使用AtomicBoolean
private static AtomicBoolean isRunning new AtomicBoolean(true);// 在循环中
while (isRunning.get()) { ... }// 设置停止
isRunning.set(false);
方案3使用synchronized方法
private static boolean isRunning true;public static synchronized boolean isRunning() {return isRunning;
}public static synchronized void stopRunning() {isRunning false;
}// 使用方式
while (isRunning()) { ... }
stopRunning();
方案4使用Lock
private static boolean isRunning true;
private static final Lock lock new ReentrantLock();public static boolean isRunning() {lock.lock();try {return isRunning;} finally {lock.unlock();}
}public static void stopRunning() {lock.lock();try {isRunning false;} finally {lock.unlock();}
} 原子性Atomicity
原子性是指一个操作或一系列操作作为一个不可分割的整体执行要么全部完成要么完全不执行。在多线程环境中非原子操作会导致竞态条件Race Condition产生不可预期的结果。
原子性三要素
不可分割操作过程不会被线程调度打断完全成功或完全失败没有中间状态状态一致性操作前后系统状态保持一致
原子性案例
1、银行账户转账经典竞态条件
package com.taiyuan.javademoone.threaddemo.demo002;/*** BankAccount 类表示一个银行账户用于演示多线程环境下的转账操作*/
public class BankAccount {// 定义账户余额private int balance;// 构造函数初始化账户余额public BankAccount(int initialBalance) {this.balance initialBalance;}/** transfer 方法用于从一个账户向另一个账户转账*/public void transfer(BankAccount dest, int amount) {// 判断账户余额是否足够if (this.balance amount) {// 模拟处理延迟放大竞态窗口try { Thread.sleep(10); } catch (InterruptedException e) {}// 扣款this.balance - amount;// 收款dest.balance amount;// 打印转账成功信息System.out.println(Thread.currentThread().getName() 转账成功: amount);}}/*** 获取账户余额*/public int getBalance() {return balance;}
}
2、测试类暴露问题
package com.taiyuan.javademoone.threaddemo.demo002;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;/*** 银行转账示例*/
public class BankTransferDemo {public static void main(String[] args) throws InterruptedException {// 创建两个账户初始余额为1000BankAccount accountA new BankAccount(1000);BankAccount accountB new BankAccount(1000);// 输出初始余额System.out.println(初始余额:);System.out.println(账户A: accountA.getBalance());System.out.println(账户B: accountB.getBalance());// 创建转账线程池ExecutorService executor Executors.newFixedThreadPool(10);// 模拟100次从A向B转账10元for (int i 0; i 100; i) {executor.execute(() - accountA.transfer(accountB, 10));}// 模拟100次从B向A转账10元for (int i 0; i 100; i) {executor.execute(() - accountB.transfer(accountA, 10));}// 关闭线程池executor.shutdown();// 等待线程池中的任务执行完毕executor.awaitTermination(1, TimeUnit.MINUTES);// 输出最终余额System.out.println(\n最终余额:);System.out.println(账户A: accountA.getBalance());System.out.println(账户B: accountB.getBalance());System.out.println(总额: (accountA.getBalance() accountB.getBalance()));}
}
测试结果 结果分析
出现总额不为2070的情况说明发生了竞态条件导致金额不一致。
3、解决方案
1、synchronized方法同步
package com.taiyuan.javademoone.threaddemo.demo003;/*** 线程安全的银行账户类使用synchronized解决原子性问题*/
public class SynchronizedBankAccount {private int balance;public SynchronizedBankAccount(int initialBalance) {this.balance initialBalance;}/*** 线程安全的转账方法** param dest 目标账户* param amount 转账金额* return 转账成功返回true否则返回false*/public boolean transfer(SynchronizedBankAccount dest, int amount) {// 按照账户对象的哈希值确定锁顺序避免死锁SynchronizedBankAccount first this.hashCode() dest.hashCode() ? this : dest;SynchronizedBankAccount second this.hashCode() dest.hashCode() ? dest : this;// 先锁定账户1再锁定账户2 避免死锁synchronized (first) {synchronized (second) {// 检查余额是否充足if (this.balance amount) {System.out.println(Thread.currentThread().getName() 转账失败: 余额不足 (当前余额 this.balance ));return false;}// 模拟处理延迟try {Thread.sleep(10);} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;}// 执行转账this.balance - amount;dest.balance amount;System.out.println(Thread.currentThread().getName() 转账成功: amount (转出账户余额 this.balance , 目标账户余额 dest.balance ));return true;}}}/*** 获取账户余额线程安全*/public synchronized int getBalance() {return balance;}
}
测试
package com.taiyuan.javademoone.threaddemo.demo003;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;/*** 银行账户转账测试类* 本类用于测试在多线程环境下两个银行账户之间的转账操作是否能够正确执行* 通过创建两个初始余额相同的银行账户使用固定数量的线程执行多次相互转账操作* 最后检查两个账户的总余额是否与初始总余额相等以此验证转账操作的线程安全性*/
public class BankTransferTest {// 初始余额常量private static final int INITIAL_BALANCE 1000;// 每次转账金额常量private static final int TRANSFER_AMOUNT 10;// 转账操作次数常量private static final int TRANSFER_COUNT 100;// 线程池线程数量常量private static final int THREAD_COUNT 10;public static void main(String[] args) throws InterruptedException {// 创建两个银行账户SynchronizedBankAccount accountA new SynchronizedBankAccount(INITIAL_BALANCE);SynchronizedBankAccount accountB new SynchronizedBankAccount(INITIAL_BALANCE);// 打印初始余额System.out.println( 初始余额 );System.out.println(账户A余额: accountA.getBalance());System.out.println(账户B余额: accountB.getBalance());System.out.println(总额: (accountA.getBalance() accountB.getBalance()));// 创建线程池ExecutorService executor Executors.newFixedThreadPool(THREAD_COUNT);// 启动转账任务System.out.println(\n 开始转账 );for (int i 0; i TRANSFER_COUNT; i) {// A向B转账executor.execute(() - accountA.transfer(accountB, TRANSFER_AMOUNT));// B向A转账executor.execute(() - accountB.transfer(accountA, TRANSFER_AMOUNT));}// 关闭线程池并等待任务完成executor.shutdown();executor.awaitTermination(1, TimeUnit.MINUTES);// 打印最终余额System.out.println(\n 最终余额 );System.out.println(账户A余额: accountA.getBalance());System.out.println(账户B余额: accountB.getBalance());// 验证总额不变int total accountA.getBalance() accountB.getBalance();int expectedTotal 2 * INITIAL_BALANCE;System.out.println(总额: 实际 total , 预期 expectedTotal (total expectedTotal ? (正确) : (错误!)));}
}运行结果 2、ReentrantLock显式锁
package com.taiyuan.javademoone.threaddemo.demo004;import java.util.concurrent.locks.ReentrantLock;/*** BankAccount 类表示一个银行账户使用ReentrantLock保证线程安全*/
public class BankLockAccount {// 账户余额private int balance;// 可重入锁private final ReentrantLock lock new ReentrantLock();// 构造函数初始化账户余额public BankLockAccount(int initialBalance) {this.balance initialBalance;}/*** transfer 方法用于从一个账户向另一个账户转账* 使用ReentrantLock保证线程安全*/public void transfer(BankLockAccount dest, int amount) {// 确定锁获取顺序例如通过hashCode比较这种通过对象哈希码决定锁顺序的方法是一种常见的死锁避免策略称为锁排序Lock Ordering。BankLockAccount first this.hashCode() dest.hashCode() ? this : dest;BankLockAccount second this.hashCode() dest.hashCode() ? dest : this;first.lock.lock();try {second.lock.lock();try {if (this.balance amount) {try { Thread.sleep(10); } catch (InterruptedException e) {}this.balance - amount;dest.balance amount;System.out.println(Thread.currentThread().getName() 转账成功: amount);}} finally {second.lock.unlock();}} finally {first.lock.unlock();}}/*** 获取账户余额*/public int getBalance() {lock.lock();try {return balance;} finally {lock.unlock();}}
}
测试
package com.taiyuan.javademoone.threaddemo.demo004;import java.util.concurrent.locks.ReentrantLock;/*** BankAccount 类表示一个银行账户使用ReentrantLock保证线程安全*/
public class BankLockAccount {// 账户余额private int balance;// 可重入锁private final ReentrantLock lock new ReentrantLock();// 构造函数初始化账户余额public BankLockAccount(int initialBalance) {this.balance initialBalance;}/*** transfer 方法用于从一个账户向另一个账户转账* 使用ReentrantLock保证线程安全*/public void transfer(BankLockAccount dest, int amount) {// 确定锁获取顺序例如通过hashCode比较这种通过对象哈希码决定锁顺序的方法是一种常见的死锁避免策略称为锁排序Lock Ordering。BankLockAccount first this.hashCode() dest.hashCode() ? this : dest;BankLockAccount second this.hashCode() dest.hashCode() ? dest : this;first.lock.lock();try {second.lock.lock();try {if (this.balance amount) {try { Thread.sleep(10); } catch (InterruptedException e) {}this.balance - amount;dest.balance amount;System.out.println(Thread.currentThread().getName() 转账成功: amount);}} finally {second.lock.unlock();}} finally {first.lock.unlock();}}/*** 获取账户余额*/public int getBalance() {lock.lock();try {return balance;} finally {lock.unlock();}}
} 3、使用AtomicReference无锁方案
package com.taiyuan.javademoone.threaddemo.demo0005;import java.util.concurrent.atomic.AtomicReference;/*** 使用AtomicReference实现的无锁银行账户*/
public class AtomicBankAccount {// 使用AtomicReference包装账户余额// AtomicReferenceInteger提供对 Integer 类型值的原子操作确保多线程环境下数据一致性。/*** AtomicReference 是 Java 中 java.util.concurrent.atomic 包的一部分* 它提供了一种原子操作方式用于更新引用类型的变量。* 与其他原子类如 AtomicInteger 和 AtomicLong类似* AtomicReference 允许你在多线程环境下安全地对对象引用进行更新* 而无需使用传统的同步机制例如 synchronized 关键字。*/private final AtomicReferenceInteger balanceRef;public AtomicBankAccount(int initialBalance) {this.balanceRef new AtomicReference(initialBalance);}/*** 无锁转账实现** param dest 目标账户* param amount 转账金额* return 转账成功返回true否则返回false*/public boolean transfer(AtomicBankAccount dest, int amount) {// 参数检查if (amount 0) {return false;}/** CAS 是一种 硬件支持的原子指令它在 Java 中通过 AtomicReference 和 Unsafe 类等机制实现。它的核心思想是* 在更新一个值时先检查该值是否与预期一致如果一致则更新为新值否则重试。*/// 使用CAS循环实现原子转账操作while (true) {// 获取当前转出账户余额的快照Integer current balanceRef.get();// 检查当前余额是否足够进行转账if (current amount) {// 余额不足时打印转账失败信息并返回false表示转账失败System.out.println(Thread.currentThread().getName() 转账失败: 余额不足);return false;}// 模拟处理转账过程中的延迟增加并发情况下冲突的可能性try {Thread.sleep((int) (Math.random() * 10));} catch (InterruptedException e) {// 当线程被中断时设置中断标志并返回false表示转账失败Thread.currentThread().interrupt();return false;}// 使用CAS操作更新转出账户的余额if (balanceRef.compareAndSet(current, current - amount)) {// 更新目标账户余额同样需要使用CAS操作以确保原子性while (true) {// 获取目标账户当前余额的快照Integer destCurrent dest.balanceRef.get();// 使用CAS操作更新目标账户余额成功则打印转账信息并返回true表示转账成功if (dest.balanceRef.compareAndSet(destCurrent, destCurrent amount)) {System.out.println(Thread.currentThread().getName() 转账成功: amount (转出账户余额 (current - amount) , 目标账户余额 (destCurrent amount) ));return true;}}}// CAS操作失败则重试整个转账过程}}public int getBalance() {return balanceRef.get();}
}
测试
package com.taiyuan.javademoone.threaddemo.demo0005;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;/*** AtomicBankTransferTest 类用于演示使用原子操作实现银行账户转账的线程安全性*/
public class AtomicBankTransferTest {// 定义初始余额常量private static final int INITIAL_BALANCE 1000;// 定义每次转账金额常量private static final int TRANSFER_AMOUNT 10;// 定义转账次数常量private static final int TRANSFER_COUNT 100;// 定义线程池大小常量private static final int THREAD_COUNT 10;/*** 主函数执行多个线程安全的转账操作* param args 命令行参数* throws InterruptedException 如果在等待线程池终止时被中断*/public static void main(String[] args) throws InterruptedException {// 初始化两个账户账户A和账户B每个账户初始余额为INITIAL_BALANCEAtomicBankAccount accountA new AtomicBankAccount(INITIAL_BALANCE);AtomicBankAccount accountB new AtomicBankAccount(INITIAL_BALANCE);// 打印初始余额System.out.println(初始余额:);System.out.println(账户A: accountA.getBalance());System.out.println(账户B: accountB.getBalance());// 创建固定大小的线程池ExecutorService executor Executors.newFixedThreadPool(THREAD_COUNT);// 启动转账任务for (int i 0; i TRANSFER_COUNT; i) {// 从账户A向账户B转账TRANSFER_AMOUNT金额executor.execute(() - accountA.transfer(accountB, TRANSFER_AMOUNT));// 从账户B向账户A转账TRANSFER_AMOUNT金额executor.execute(() - accountB.transfer(accountA, TRANSFER_AMOUNT));}// 关闭线程池并等待所有任务完成executor.shutdown();// 等待所有任务完成executor.awaitTermination(1, TimeUnit.MINUTES);// 打印最终余额System.out.println(\n最终余额:);System.out.println(账户A: accountA.getBalance());System.out.println(账户B: accountB.getBalance());System.out.println(总额: (accountA.getBalance() accountB.getBalance()));}
}有序性(Ordering)
有序性ordering指程序执行的顺序必须符合预期不能出现乱序的情况。在多线程环境下由于编译器、处理器、缓存等因素的影响程序执行的顺序可能会出现不一致的情况导致程序出现错误。为了保证有序性可以使用volatile关键字或者显式地使用锁来实现。同时Java提供了happens-before规则它可以保证在特定情况下操作的顺序是按照预期的顺序执行的。
导致有序性问题的三大原因
编译器优化重排序在不改变单线程语义的前提下编译器可能调整指令顺序处理器指令级并行现代CPU采用流水线、乱序执行等技术内存系统重排序由于多级缓存的存在内存操作可能表现出乱序行为 Java提供了多种机制来保证有序性
1. volatile关键字
volatile关键字可以
禁止指令重排序通过内存屏障实现保证变量的可见性
2. synchronized关键字
synchronized通过互斥锁保证
同一时刻只有一个线程能访问同步代码块在进入同步块前会清空工作内存退出时会将变量刷新回主内存禁止指令重排序
3. final关键字
正确初始化的final字段可以保证
在构造函数完成初始化后对其他线程可见禁止对final字段的写操作重排序到构造函数之外
4. happens-before原则
Java内存模型定义的happens-before关系保证了有序性包括
程序顺序规则监视器锁规则volatile变量规则线程启动规则线程终止规则线程中断规则对象终结规则传递性
5. 原子类
java.util.concurrent.atomic包下的原子类使用CAS操作和volatile语义保证有序性。 6、什么是happens-before原则
Happens-Before原则是Java内存模型(JMM)的核心理论它从语言层面定义了多线程环境中操作之间的可见性规则为开发者提供了一种理解并发程序行为的框架。
Happens-Before关系本质上是一种可见性保证契约
如果操作A happens-before 操作B那么A的所有写操作对B都是可见的这种关系可以跨线程传递JVM必须遵守这些规则但可以在不违反规则的前提下进行优化
Happens-Before先行发生原则的定义
程序次序规则Program Order Rule在一个线程内按照控制流顺序书写在前面的操作先行发生于书写在后面的操作。管程锁定规则Monitor Lock Rule一个unlock操作先行发生于后面对同一个锁的lock操作。volatile变量规则Volatile Variable Rule对一个volatile变量的写操作先行发生于后面对这个变量的读操作。线程启动规则Thread Start RuleThread对象start()方法先行发生于此线程的每一个动作。线程终止规则Thread Termination Rule线程中的所有操作都先行发生于对此线程的终止检测我们可以通过Thread.join()方法和Thread.isAlive()的返回值等手段检测线程是否已经终止执行。线程中断规则Thread Interruption Rule对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生可以通过Thread.interrupted()方法检测到是否有中断发生。对象终结规则Finalizer Rule 一个对象的初始化完成构造函数结束先行发生于它的finalize()方法的开始。传递性Transitivity如果操作A先行发生于操作B操作B先行发生于操作C那就可以得出操作A先行发生于操作C的结论。
7、volatile、synchronized 和 final详解
1. volatile关键字
特性
可见性保证变量的修改对所有线程立即可见有序性禁止指令重排序通过内存屏障实现不保证原子性复合操作仍需同步
适用场景
单写多读的场景作为状态标志位双重检查锁定模式(DCL)
2. synchronized关键字
特性
互斥性同一时刻只有一个线程能进入同步块可见性同步块内的变量修改对其他线程可见原子性保证代码块内的操作不可分割有序性禁止指令重排序
使用方式
同步实例方法同步静态方法同步代码块
3、final关键字
特性
不可变性基本类型值不可变引用类型引用不可变线程安全正确构造的对象对所有线程可见初始化安全禁止final字段的写操作重排序到构造函数之外
使用场景
常量定义不可变对象安全发布对象