上海网站建设做物流一WordPress页面模板怎么选
- 作者: 五速梦信息网
- 时间: 2026年03月21日 09:24
当前位置: 首页 > news >正文
上海网站建设做物流一,WordPress页面模板怎么选,wordpress文章备份插件,网站登陆模板下载目录
一.多线程下数据不一致问题
二.锁和synchronized
2.1 并发编程三大特性
2.2引入锁概念
三.synchronized的锁实现原理
3.1 monitorenter和monitorexit
3.2synchronized 锁的升级
3.2.1偏向锁的获取和撤销
3.2.2轻量级锁的加锁和解锁
自适应自旋锁
轻量级锁的解锁…目录
一.多线程下数据不一致问题
二.锁和synchronized
2.1 并发编程三大特性
2.2引入锁概念
三.synchronized的锁实现原理
3.1 monitorenter和monitorexit
3.2synchronized 锁的升级
3.2.1偏向锁的获取和撤销
3.2.2轻量级锁的加锁和解锁
自适应自旋锁
轻量级锁的解锁
3.2.3重量级锁—线程阻塞
3.2.4锁的优缺点对比
3.3CAS实现原子性 一.多线程下数据不一致问题
我们知道在并发编程中多个线程同时访问共享资源时可能导致数据不一致、死锁、性能问题等严重后果。
像下面这个例子i的值最后是多少呢 简单的demo实验下
public class Demo {private static int count 0;public static void inc() {try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}count;}
public static void main(String[] args) throws InterruptedException {for (int i 0; i 100; i) {new Thread(() - Demo.inc()).start();}Thread.sleep(3000);System.out.println(运行结果 count);}
}
多次的运行结果是不相同的并不是预期的100 针对数据不一致的问题我们先了解下java内存模型(Java Memory Model, JMM),它是一种抽象的模型被定义出来屏蔽各种硬件和操作系统的内存访问差异。JMM定义了线程与主内存之间的抽象关系线程之间的共享变量存储在主内存中而每个线程都有一个私有的本地内存本地内存中存储了该线程以读/写共享变量的副本。 结合JMM不难理解线程t1和t2通过操作本地内存中的副本进行刷新存储在主内存上的共享变量对象但它们都没有在进行修改操作后立即告知其它线程。针对多线程并发访问共享资源导致数据不一致的问题java给出了类似Synchronized和volatile的解决方案。
二.锁和synchronized
2.1 并发编程三大特性
并发编程中有三大核心特性分别是原子性Atomicity、可见性Visibility、有序性Ordering通常被简称为 AVO 特性。这些特性对于多线程编程非常重要因为它们确保了多线程环境下的正确性和可靠性。 原子性Atomicity 原子性指的是一个操作是不可分割的单元要么全部执行成功要么全部不执行不会被中断。原子操作可以看作是线程安全的多个线程可以同时执行这个操作而不会破坏操作的完整性。 原子性保证了操作的完整性避免了竞态条件通常通过锁、原子类、事务等机制来实现。 可见性Visibility 可见性指的是一个线程对共享变量的修改能够被其他线程立即感知到即修改后的值在主内存中对其他线程是可见的。在多线程环境下如果不采取适当的同步措施共享变量的修改可能对其他线程不可见导致意外的行为和错误。 可以使用volatile关键字来确保变量的可见性或者使用锁机制如synchronized关键字来同步访问共享变量。 有序性Ordering 有序性指的是程序的执行顺序与代码的编写顺序一致即代码按照预期的顺序执行不会出现乱序或重排序的情况。 在现代计算机架构中为了提高性能编译器和处理器可能会对指令进行重排序但这种重排序在单线程环境下不会引发问题。然而在多线程环境下如果不正确地控制重排序可能会导致不一致的结果。 可以使用同步机制来确保有序性例如在进入和退出锁的范围内编译器和处理器会执行必要的指令重排序来维护有序性。
2.2引入锁概念
在可见性的理解上java引入了锁的概念用于防止多个线程同时访问或修改共享资源以确保线程安全性。 而加锁的方式是使用synchronized关键字。
public class Demo {private static int count 0;// 使用Synchronized加锁public static synchronized void inc() {try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}count;}
public static void main(String[] args) throws InterruptedException {for (int i 0; i 100; i) {new Thread(() - Demo.inc()).start();}Thread.sleep(3000);System.out.println(运行结果 count);}
} synchronized 关键字在 Java 中主要用于实现互斥锁它主要保证了并发编程中的原子性和可见性这两个特性。 原子性Atomicity synchronized 保证了被 synchronized 修饰的代码块或方法在同一时间只能被一个线程执行这确保了其中的操作是原子的即不会被中断或同时被其他线程访问。这有助于防止竞态条件和确保操作的完整性。 可见性Visibility 当一个线程进入 synchronized 块或方法时它会获取锁并在释放锁时将修改的数据从线程的工作内存同步到主内存这就确保了其他线程可以立即看到最新的数据。这满足了可见性要求确保了共享数据的变更对其他线程是可见的。
synchronized 关键字在 Java 中有三种主要的用法用于实现不同类型的同步 实例方法同步 使用 synchronized 关键字修饰普通的实例方法这会将锁定范围限制在该方法所属对象实例上即同一对象的不同线程在同时访问这个方法时会互斥执行。这种方式适用于对实例变量的同步访问。 public synchronized void instanceMethod() {// 这里的代码是线程安全的
} 静态方法同步 使用 synchronized 关键字修饰静态方法这会将锁定范围限制在类的 Class 对象上即同一类的不同对象的不同线程在同时访问这个静态方法时会互斥执行。这种方式适用于对静态变量的同步访问。 public static synchronized void staticMethod() {// 这里的代码是线程安全的
} 同步代码块 使用 synchronized 关键字修饰代码块可以精确地指定需要同步的对象。这允许你在方法内的特定部分实现同步而不是整个方法。你需要指定一个对象作为锁当多个线程尝试进入同步代码块时它们必须获取相同的锁才能执行。 public void someMethod() {// 一些非同步代码synchronized (lockObject) {// 这里的代码是线程安全的锁定的是 lockObject 对象}// 更多非同步代码
} 设计模式中 [单例模式的双重检查锁定] https://blog.csdn.net/Elaine2391/article/details/132675080 其实就是一个简单的Synchronized和volatile的应用。
三.synchronized的锁实现原理
3.1 monitorenter和monitorexit
我们使用synchronized的时候没有手动的lock和unlock那么synchronized是怎么加锁的呢其实这里JVM帮我们处理了。反编译⼀段synchronized修饰代码块代码来看看
找到上面我们的Demo类文件路径javac Demo.java后生成class文件 javap -v Demo.class 可以看到相应的字节码指令。synchronized修饰静态方法时JVM采用monitorenter和monitorexit两个指令来实现同步monitorenter 指令指向同步代码块的开始位置 monitorexit 指令则指向同步代码块的结束位置。 而且synchronized修饰同步方法时JVM采用ACC_SYNCHRONIZED标记符实现同步这个标识指明了该方法是一个同步方法。 monitorenter、monitorexit或者ACC_SYNCHRONIZED都是基于Monitor实现的。Monitor是什么呢
所谓的Monitor其实是⼀种同步⼯具也可以说是⼀种同步机制。在Java虚拟机HotSpot中Monitor是由 ObjectMonitor实现的可以叫做内部锁或者Monitor锁。线程在获取锁的时候实际上就是获得一个监视器对象(monitor)monitor可以认为是一个同步对象所有的Java对象是天生携带monitor。多个线程访问同步代码块时相当于去争抢对象监视器修改对象中的锁标识ObjectMonitor这个对象和线程争抢锁的逻辑有密切的关系。
3.2synchronized 锁的升级
在JVM的自动内存管理中分析markword时提到了偏向锁、轻量级锁、重量级锁和无锁状态。分析前我们先来思考一个问题使用锁能够实现数据的安全性但是会带来性能的下降。不使用锁能提高性能又不能保证线程安全性。怎么办呢
其实hotspot的作者经过调查发现假设加锁的同步块分为下面三种情况而大部分情况下是处于第一种。 只有线程A进入临界区。偏向锁 线程A和线程B交替进入临界区。轻量级锁 线程A、线程B、线程C同时进入临界区。重量级锁
而这也是JDK1.6之后synchronized做出的优化为了减少获得锁和释放锁带来的性能开销引入了偏向锁、轻量级锁的概念。所以synchronized中锁存在四种状态分别是无锁、偏向锁、轻量级锁、重量级锁锁的状态根据竞争激烈的程度从低到高不断升级。
针对共享资源对象加锁的操作其实真正操作的是对象头中的markword。 3.2.1偏向锁的获取和撤销 首先获取锁对象的Markword判断是否处于可偏向状态。(偏向锁位biased_lock0且ThreadId为空表示未偏向任何线程 如果是可偏向状态则通过CAS操作把当前线程的ID写入到Markword a如果CAS成功那么就表示线程获取了锁对象的偏向锁锁状态从无锁升级为偏向锁。 b如果CAS失败说明其他线程已获得了偏向锁。这种情况说明当前存在锁竞争需要暂停已获得偏向锁的线程撤销偏向锁升级为轻量级锁(这个操作在全局安全点执行就是没有线程在执行字节码的时候) 如果是已偏向状态需要检查Markword中存储的ThreadId是否等于当前线程的ThreadId a如果相等不需要再次获得锁可直接执行同步代码块。这就避免了锁的竞争和线程上下文切换。 b如果不相等说明当前锁偏向于其他线程需要撤销锁并升级为轻量级锁
对于原持有偏向锁的线程进行撤销时原获得偏向的线程有两种情况
a原获得偏向锁的线程已经退出了临界区就是执行完了代码块这时候就会把对象头设置为无锁状态并且争抢锁的进程可以基于CAS重新设置对象头偏向当前线程。
b原获得偏向锁的线程还在执行同步块这个时候就会将原获得偏向锁的线程的偏向锁升级为轻量级锁。
但是根据我们实际情况绝大部分时候一定会存在2个以上的线程竞争那么开启偏向锁反而提升了获取锁的资源消耗可以通过jvm参数UserBiasedLocking来设置开启或关闭偏向锁。流程图分析如下 3.2.2轻量级锁的加锁和解锁 首先jvm会判断是否是重量级锁如果不是会在当前线程栈帧中划出一块空间作为该锁的锁记录并且将锁对象Markword复制到该锁记录中。 复制成功后jvm使用CAS操作将对象头Markword更新为指向锁记录的指针并且将锁记录里的own指针指向对象头的Markword。 更新对象头Markword成功的情况当前线程持有对象锁并且对象Markword锁标志设为‘00’即表示此对象处于轻量级锁状态。 更新对象头Markword失败的情况jvm先检查对象MarkWord是否指向当前线程栈帧中的锁记录如果是就表示锁重入。如果不是就表示锁对象被其他线程抢占进行自旋等待(默认10次)等待次数达到阈值仍未获取到锁膨胀为重量级锁。
自适应自旋锁
自旋锁就是锁在原地循环执行一个啥都没有的for循环操作是会消耗cpu的。JDK1.6后引入了自适应自旋锁自适应意味着着自旋的次数不是固定不变的而是根据前一次在同一个锁上自旋的时间以及锁的拥有者的状态来决定。
如果在同一个锁对象上自旋等待刚刚成功获得过锁并且持有锁的线程正在运行中那么虚拟机就会认为这次自
旋也是很有可能再次成功进而它将允许自旋等待持续相对更长的时间。
如果对于某个锁自旋很少成功获得过那在以后尝试获取这个锁时将可能省略掉自旋过程直接阻塞线程避免浪费处理器资源。
轻量级锁的解锁
就是获得锁的逆向逻辑通过CAS操作把栈帧中的LockRecord替换回锁对象的Markword中如果成功表示没有竞争。如果失败表示当前锁存在竞争那么轻量级锁就会膨胀为重量级锁。流程图如下 3.2.3重量级锁—线程阻塞
轻量级锁膨胀到重量级锁后线程只能被挂起阻塞来等待被唤醒了。阻塞是重量级锁的标志。重量级锁加锁的基本流程 举例说明下
定义线程A传入锁对象lock包含一个简单的wait操作。
public class ThreadA extends Thread{private Object lock;public ThreadA(Object lock){this.lock lock;}Overridepublic void run() {synchronized (lock) {System.out.println(ThreadA start);try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(ThreadA end);}}
}
定义线程B传入锁对象包含一个notify操作。
public class ThreadB extends Thread{private Object lock;public ThreadB(Object lock){this.lock lock;}Overridepublic void run() {synchronized (lock) {System.out.println(ThreadB start);lock.notify();System.out.println(ThreadB end);}}
}
创建A线程启动B线程启动执行结果
public class WaitNotify {public static void main(String[] args) {Object lock new Object();ThreadA threadA new ThreadA(lock);threadA.start();
ThreadB threadB new ThreadB(lock);threadB.start();}
} 3.2.4锁的优缺点对比 3.3CAS实现原子性 CAS叫做CompareAndSwap⽐较并交换主要是通过处理器的指令来保证操作的原⼦性的。 CAS 指令包含 3 个参数共享变量的内存地址 A、预期的值 B 和共享变量的新值 C。 只有当内存中地址 A 处的值等于 B 时才能将内存中地址 A 处的值更新为新值 C。作为⼀条 CPU 指令CAS 指令 本身是能够保证原⼦性的
CAS操作三大问题 ABA问题在比较期间发生了A-B-ACAS感知不到过程解决方案加版本号。1A-2B-3A. 循环时间长开销大 只能保证一个共享变量的原子操作
- 上一篇: 上海网站建设最好的公司排名惠州网站建设欧力虎
- 下一篇: 上海网站开发报价专业沈阳网站制作
相关文章
-
上海网站建设最好的公司排名惠州网站建设欧力虎
上海网站建设最好的公司排名惠州网站建设欧力虎
- 技术栈
- 2026年03月21日
-
上海网站建设找摩彼网站返回顶部代码
上海网站建设找摩彼网站返回顶部代码
- 技术栈
- 2026年03月21日
-
上海网站建设招聘大鹏新区住房和建设局网站
上海网站建设招聘大鹏新区住房和建设局网站
- 技术栈
- 2026年03月21日
-
上海网站开发报价专业沈阳网站制作
上海网站开发报价专业沈阳网站制作
- 技术栈
- 2026年03月21日
-
上海网站开发从化网站制作
上海网站开发从化网站制作
- 技术栈
- 2026年03月21日
-
上海网站哪个比较好无人在线观看高清完整视频下载
上海网站哪个比较好无人在线观看高清完整视频下载
- 技术栈
- 2026年03月21日
