太原市城乡建设局网站,模版网站后期可以更换图片吗,服装定制一般多少钱,wordpress过滤插件目录
一、java中的线程同步
二、Synchronized
使用方式
底层原理
synchronized 同步代码块的情况
synchronized 修饰方法的情况
总结
synchronized 和 volatile 有什么区别#xff1f;
三、ReentrantLock
底层原理
使用方式
四、Synchronized和 ReentrantLock有什…
目录
一、java中的线程同步
二、Synchronized
使用方式
底层原理
synchronized 同步代码块的情况
synchronized 修饰方法的情况
总结
synchronized 和 volatile 有什么区别
三、ReentrantLock
底层原理
使用方式
四、Synchronized和 ReentrantLock有什么区别?
两者都是可重入锁
1synchronized依赖于JVM而ReentrantLock依赖于API
2ReentrantLock比synchronized增加了一些高级功能
3获取锁和释放锁方式不同
4锁类型不同
5响应中断不同
6底层实现不同
小结 一、java中的线程同步
线程同步是指在多线程环境下为了避免多个线程对共享资源进行同时访问从而引发数据不一致或其他问题的一种机制。它通过对关键代码段加锁使得同一时刻只有一个线程能够访问共享资源。
当多个线程共享同一资源如变量、对象或文件时若没有同步机制可能会导致竞态条件即线程对共享资源的操作是非原子性的多个线程之间可能会同时修改数据导致结果不符合预期。 二、Synchronized
使用方式
synchronized 关键字的使用方式主要有下面 3 种
修饰实例方法修饰静态方法修饰代码块
1修饰实例方法 锁当前对象实例
给当前对象实例加锁进入同步代码前要获得 当前对象实例的锁 。
synchronized void method() {//业务代码
}
2修饰静态方法 锁当前类
给当前类加锁会作用于类的所有对象实例 进入同步代码前要获得 当前 class 的锁。
这是因为静态成员不属于任何一个实例对象归整个类所有不依赖于类的特定实例被类的所有实例共享。
synchronized static void method() {//业务代码
}
静态 synchronized 方法和非静态 synchronized 方法之间的调用互斥么不互斥如果一个线程 A 调用一个实例对象的非静态 synchronized 方法而线程 B 需要调用这个实例对象所属类的静态 synchronized 方法是允许的不会发生互斥现象因为访问静态 synchronized 方法占用的锁是当前类的锁而访问非静态 synchronized 方法占用的锁是当前实例对象锁。
3修饰代码块 锁指定对象/类
对括号里指定的对象/类加锁
synchronized(object) 表示进入同步代码库前要获得 给定对象的锁。synchronized(类.class) 表示进入同步代码前要获得 给定 Class 的锁
synchronized(this) {//业务代码
}
总结
synchronized 关键字加到 static 静态方法和 synchronized(class) 代码块上都是是给 Class 类上锁synchronized 关键字加到实例方法上是给对象实例上锁尽量不要使用 synchronized(String a) 因为 JVM 中字符串常量池具有缓存功能。构造方法不能使用 synchronized 关键字修饰。不过可以在构造方法内部使用 synchronized 代码块。另外构造方法本身是线程安全的但如果在构造方法中涉及到共享资源的操作就需要采取适当的同步措施来保证整个构造过程的线程安全。
底层原理
synchronized 关键字底层原理属于 JVM 层面的东西。
synchronized 同步代码块的情况
public class SynchronizedDemo {public void method() {synchronized (this) {System.out.println(synchronized 代码块);}}
}
通过 JDK 自带的 javap 命令查看 SynchronizedDemo 类的相关字节码信息首先切换到类的对应目录执行 javac SynchronizedDemo.java 命令生成编译后的 .class 文件然后执行javap -c -s -v -l SynchronizedDemo.class。 从上面我们可以看出synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令其中 monitorenter 指令指向同步代码块的开始位置monitorexit 指令则指明同步代码块的结束位置。
上面的字节码中包含一个 monitorenter 指令以及两个 monitorexit 指令这是为了保证锁在同步代码块代码正常执行以及出现异常的这两种情况下都能被正确释放。
当执行 monitorenter 指令时线程试图获取锁也就是获取 对象监视器 monitor 的持有权。 在 Java 虚拟机(HotSpot)中Monitor 是基于 C实现的由ObjectMonitor实现的。每个对象中都内置了一个 ObjectMonitor对象。 另外wait/notify等方法也依赖于monitor对象这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法否则会抛出java.lang.IllegalMonitorStateException的异常的原因。 在执行monitorenter时会尝试获取对象的锁如果锁的计数器为 0 则表示锁可以被获取获取后将锁计数器设为 1 也就是加 1。 对象锁的的拥有者线程才可以执行 monitorexit 指令来释放锁。在执行 monitorexit 指令后将锁计数器设为 0表明锁被释放其他线程可以尝试获取锁。 如果获取对象锁失败那当前线程就要阻塞等待直到锁被另外一个线程释放为止。
synchronized 修饰方法的情况
public class SynchronizedDemo2 {public synchronized void method() {System.out.println(synchronized 方法);}
} synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令取而代之的是 ACC_SYNCHRONIZED 标识该标识指明了该方法是一个同步方法。JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法从而执行相应的同步调用。
如果是实例方法JVM 会尝试获取实例对象的锁。如果是静态方法JVM 会尝试获取当前 class 的锁。
总结
synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令其中 monitorenter 指令指向同步代码块的开始位置monitorexit 指令则指明同步代码块的结束位置。
synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令取而代之的是 ACC_SYNCHRONIZED 标识该标识指明了该方法是一个同步方法。
不过两者的本质都是对对象监视器 monitor 的获取。 synchronized 和 volatile 有什么区别
synchronized 关键字和 volatile 关键字是两个互补的存在而不是对立的存在
volatile 关键字是线程同步的轻量级实现所以 volatile性能肯定比synchronized关键字要好 。但是 volatile 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块 。volatile 关键字能保证数据的可见性但不能保证数据的原子性。synchronized 关键字两者都能保证。volatile关键字主要用于解决变量在多个线程之间的可见性而 synchronized 关键字解决的是多个线程之间访问资源的同步性。 三、ReentrantLock
ReentrantLock 实现了 Lock 接口是一个可重入且独占式的锁和 synchronized 关键字类似。不过ReentrantLock 更灵活、更强大增加了轮询、超时、中断、公平锁和非公平锁等高级功能。
public class ReentrantLock implements Lock, java.io.Serializable {}
ReentrantLock 里面有一个内部类 SyncSync 继承 AQSAbstractQueuedSynchronizer添加锁和释放锁的大部分操作实际上都是在 Sync 中实现的。Sync 有公平锁 FairSync 和非公平锁 NonfairSync 两个子类。 ReentrantLock 默认使用非公平锁也可以通过构造器来显式的指定使用公平锁。
// 传入一个 boolean 值true 时为公平锁false 时为非公平锁
public ReentrantLock(boolean fair) {sync fair ? new FairSync() : new NonfairSync();
}
底层原理
从上面的内容可以看出 ReentrantLock 的底层就是由 AQS 来实现的。
使用方式
1ReentrantLock 在使用之前需要先创建 ReentrantLock 对象然后使用 lock 方法进行加锁使用完之后再调用 unlock 方法释放锁具体使用如下
import java.util.concurrent.locks.ReentrantLock;public class LockExample {private final ReentrantLock lock new ReentrantLock();public void lockedMethod() {lock.lock(); // 获取锁try {// 线程安全的代码System.out.println(Inside locked method);} finally {lock.unlock(); // 确保释放锁}}
}2ReentrantLock 支持公平锁和非公平锁可以通过构造函数指定锁的获取策略。此外它还提供了尝试锁定、可中断锁定等高级功能。
import java.util.concurrent.locks.ReentrantLock;public class FairLockExample {private final ReentrantLock fairLock new ReentrantLock(true); // 公平锁public void fairLockedMethod() {fairLock.lock();try {// 线程安全的代码System.out.println(Inside fair locked method);} finally {fairLock.unlock();}}
}3ReentrantLock 提供了 Condition 类可以创建多个条件变量提供了更灵活的线程通信机制。如下代码
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class ConditionExample {private final ReentrantLock lock new ReentrantLock();private final Condition condition lock.newCondition();public void waitMethod() {lock.lock();try {// 等待条件condition.await();System.out.println(Condition met, resuming...);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void signalMethod() {lock.lock();try {// 触发条件condition.signal();System.out.println(Condition signaled);} finally {lock.unlock();}}
}四、Synchronized和 ReentrantLock有什么区别?
在 Java 中常用的锁有两种synchronized和 ReentrantLock二者的功效相同但又有很多不同点。
两者都是可重入锁
可重入锁 也叫递归锁指的是线程可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁此时这个对象锁还没有释放当其再次想要获取这个对象的锁的时候还是可以获取的如果是不可重入锁的话就会造成死锁。
JDK 提供的所有现成的 Lock 实现类包括 synchronized 关键字锁都是可重入的。
public class SynchronizedDemo {public synchronized void method1() {System.out.println(方法1);method2();}public synchronized void method2() {System.out.println(方法2);}
}
代码中method1() 和 method2()都被 synchronized 关键字修饰method1()调用了method2()。
由于 synchronized锁是可重入的同一个线程在调用method1() 时可以直接获得当前对象的锁执行 method2() 的时候可以再次获取这个对象的锁不会产生死锁问题。1synchronized依赖于JVM而ReentrantLock依赖于API
synchronized是依赖于 JVM实现的前面我们也讲到了虚拟机团队在 JDK1.6为synchronized 关键字进行了很多优化但是这些优化都是在虚拟机层面实现的并没有直接暴露给我们。
ReentrantLock是JDK层面实现的也就是API层面需要lockO和unlockO方法配合try/finally语句块来完成所以我们可以通过查看它的源代码来看它是如何实现的。
2ReentrantLock比synchronized增加了一些高级功能
相比synchronizedReentrantLock 增加了一些高级功能。主要来说主要有三点:
等待可中断ReentrantLock提供了一种能够中断等待锁的线程的机制通过lock.lockInterruptibly()来实现。也就是说正在等待的线程可以选择放弃等待改为处理其他事情。可实现公平锁ReentrantLock可以指定是公平锁还是非公平锁。而 synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。ReentrantLock默认情况是非公平的可以通过ReentrantLock类的ReentrantLock(booleanfair构造方法来指定是否是公平的。可实现选择性通知锁可以绑定多个条件synchronized关键字与wait()和notify()/notifyAll()方法相结合可以实现等待/通知机制。ReentrantLock类当然也可以实现但是需要借助于Condition 接口与 newCondition方法。 Condition是 JDK1.5 之后才有的它具有很好的灵活性比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例即对象监视器线程对象可以注册在指定的Condition中从而可以有选择性的进行线程通知在调度线程上更加灵活。 在使用notify()/notifyAll()方法进行通知时被通知的线程是由 JVM 选择的用ReentrantLock类结合Condition实例可以实现“选择性通知” 这个功能非常重要而且是 Condition 接口默认提供的。 而synchronized关键字就相当于整个 Lock 对象中只有一个Condition实例所有的线程都注册在它一个身上。如果执行notifyAll()方法的话就会通知所有处于等待状态的线程这样会造成很大的效率问题。而Condition实例的signalAll()方法只会唤醒注册在该Condition实例中的所有等待线程。 3获取锁和释放锁方式不同
synchronized 会自动加锁和释放锁当进入 synchronized 修饰的代码块之后会自动加锁当离开 synchronized 的代码段之后会自动释放锁如下图所示 而 ReentrantLock 需要手动加锁和释放锁如下图所示 4锁类型不同
synchronized 属于非公平锁而 ReentrantLock 既可以是公平锁也可以是非公平锁。
默认情况下 ReentrantLock 为非公平锁这点查看源码可知 使用 new ReentrantLock(true) 可以创建公平锁查看源码可知 5响应中断不同
ReentrantLock 可以使用 lockInterruptibly 获取锁并响应中断指令而 synchronized 不能响应中断也就是如果发生了死锁使用 synchronized 会一直等待下去而使用 ReentrantLock 可以响应中断并释放锁从而解决死锁的问题。
PS在使用 ReentrantLock 时要特别小心unlock 释放锁的操作一定要放在 finally 中否者有可能会出现锁一直被占用从而导致其他线程一直阻塞的问题。 6底层实现不同
synchronized 是 JVM 层面通过监视器Monitor实现的而 ReentrantLock 是通过 AQSAbstractQueuedSynchronizer程序级别的 API 实现。
synchronized 通过监视器实现。其中 monitorenter 表示进入监视器相当于加锁操作而 monitorexit 表示退出监视器相当于释放锁的操作。
而ReentrantLock 是通过 AQS 实现。
小结
synchronized 和 ReentrantLock 都是 Java 中提供的可重入锁二者的主要区别有以下 5 个
用法不同synchronized 可以用来修饰普通方法、静态方法和代码块而 ReentrantLock 只能用于代码块。获取锁和释放锁的机制不同synchronized 是自动加锁和释放锁的而 ReentrantLock 需要手动加锁和释放锁。锁类型不同synchronized 是非公平锁而 ReentrantLock 默认为非公平锁也可以手动指定为公平锁。响应中断不同ReentrantLock 可以响应中断解决死锁的问题而 synchronized 不能响应中断。底层实现不同synchronized 是 JVM 层面通过监视器实现的而 ReentrantLock 是基于 AQS 实现的。