初识并发编程【2】之多线程synchronized、Volatile
本文源自Recently祝祝,创自Recently祝祝。转载请标注出处。
此解决方式在企业中有所应用,适合Java初级开发学习,参考。
本文字数与行数,耐心阅读必有收获。
说到线程就肯定会说到线程上下文、跟线程安全,在前一篇文章中也对这些部分有过说明了,关于多线程的线程安全这篇文章就来说synchronized跟Volatile。每天学习的知识点做一个完整的分享,希望对你有用。
为什么要提及线程安全呢?这就可以用我们很熟悉的购票系统说明问题了,当铁路部门开始售票的时候,所有线程系统是随机分配随机调度的,可能同一个线程抢同一张票,可能线程抢占了不存在的票,这些情况都是有可能的,也就是多线程访问共享资源,但是还来不及同步,这种情况下其它线程可能都在尝试获取同一张票并且标记已售出。所以就会存在不同线程抢到同一张票的可能,这也就是我们常说的线程安全问题。
1.使用多线程的重要性
先来说说多线程,多线程就是多个线程并发或者并行的再进行处理请求。当数据量大的时候单线程是支撑不过来的,这其中包括了IO请求,并且还有线程上下文切换的资源消耗,在数据量大的时候,这些资源消耗所占用的时间就会被放大
多线程的好处:减少线程上下文切换带来的开销、提高系统性能、提高系统执行效率。
多线程带来的问题:线程安全、死锁等
2.锁说明
同步锁一共有四种状态,级别从低到高依次是:无锁,偏向锁,轻量级锁,重量级锁。这四种状态会随着竞争激烈情况逐渐升级。
锁类型,当前持有线程
3.synchronized
解决线程安全方式一:synchronized同步机制。在前面也说了同步跟异步的区别,同步需要线程又返回才会执行下一个线程,否则就处于等待状态。synchronized解决多个线程访问的同步性
synchronized可以用修饰在方法(静态方法、实例方法)上,也可以用修饰在代码块上。其实这三种修饰都是在上锁。除了实例方法,synchronized(object)是给对象上锁,其他修饰今天方法,synchronized(.class)都是给java类上锁。synchronized只能用于修饰这三种,其他的不能被synchronized修饰。
同步方法:
//同步方法
public synchronized void method(){
//可能会产生线程安全问题的代码
}
同步代码:
//方法内部使用
synchronized(同步锁){
需要同步操作的代码
}
2.1实例
public class ShimmerSynchronized {
public synchronized void increase(){
System.out.println("synchronized 实例方法");
}
public void syncBlock(){
synchronized (this){
System.out.println("synchronizedd代码块");
}
}
}
然后通过javap -c ShimmerSynchronized.java
可以查看生成class文件对应的字节码指令
可以查看指令中有两个monitorexit,在finally中会调用monitorexit命令释放锁
4.Volatile
解决线程安全的方式二:volatile也是一个解决线程安全的方案,并且比synchronized资源消耗量更少,成本更低。volatile是不上锁的线程安全解决方式
前边说了多线程的特性,但是volatile
关键字能保证数据的可见性,但不能保证数据的原子性。synchronized
关键字两者都能保证。
// 添加volatile关键词
private volatile boolean flag = true;
线程写volatile变量的过程:
-
改变线程本地内存中volatile变量副本的值;
-
将改变后的副本的值从本地内存刷新到主内存
线程读volatile变量的过程:
-
从主内存中读取volatile变量的最新值到线程的本地内存中
-
从本地内存中读取volatile变量的副本
使用:
-
变量真正独立于其他变量和自己以前的值,在单独使用的时候,适合用volatile
-
对变量的写入操作不依赖其当前值:例如++和--运算符的场景则不行
-
该变量没有包含在具有其他变量的不变式中
volatile比synchronized复杂,但是两者的使用是不同的,具备不一样的特性,需要根据自己的业务需求来选择。
文章全为个人理解,如果发现部分跟你所知道的有出入,欢迎在评论区指出,欢迎探讨。
转载自:https://juejin.cn/post/7229704377408585785