多线程基础与synchronized原理

Java运行原理分析

在这里插入图片描述
聊多线程不得不聊Java是如何运行的,怎么运行又牵扯到了JVM这个东西,我们先分析一下,jvm的内存结构图与作用看我这篇文章了解一下就行
https://blog.csdn.net/weixin_44329272/article/details/104229151

多线程的状态

在这里插入图片描述
六种状态,三种变化

线程状态,六种:新建,运行,等待,超时等待,阻塞,终止

一般三种变化情况,
1.新建–>运行–>终止
2.新建–>运行–>等待/超时等待–>运行–>终止
3.新建–>运行–>阻塞–>运行–>终止

什么时候是等待状态wait这种,什么时候是阻塞,锁或者sellp的时候是,阻塞状态,等待状态会释放CPU控制权,阻塞状态不会,他会霸占使用权

线程正确的终止方式

线程的正确终止方式,是interrupt,不是stop方法,stop方法强制终止会有线程安全问题,同步关键字中,在终止之前的操作不会撤销,而导致数据不一致性

线程通信

线程通信的三组API:

suspend/resume:已经被弃用的一组api,主要原因是太容易写出死锁代码了,在同步关键字中suspend使用的是挂起状态,而无法被resume争抢到这个锁,而无法释放,并且对先后执行顺序也有要求

wait/notify:他解决了,同步关键字的而导致死锁的问题,他不是将线程挂起,是将锁资源释放进入等待的一种方式,另一个同步块中还是可以争抢这个锁对象,但是对先后执行循序有要求,不能在wait之前,执行了线程挂起并执行线程释放操作,否则还是会死锁

park/unpark:他解决了,线程执行先后循序,但是由于他是,线程挂起的一种操作方式,同步关键字也会让他造成死锁

线程封闭与栈封闭

线程封闭:是ThreadLoca实现的,他将每一个线程当成一个小单元,互不影响,其他线程也无法访问到其他线程中的单元,从而保证了线程安全

栈封闭:利用的是JVM机制,每个线程对应一个栈,从而不会有线程安全,例如局部变量

线程可见性,与CPU指令重排

可见性问题是什么:一个线程正在写入,另一个线程无法感觉到这个操作

怎么解决线程可见性:使用volatile关键字 ,
CPU指令重排是一种对,代码执行的优化手段,但是在多线程环境下,指令重排容易会有问题,使用volatile禁止指令重排

线程原子性操作

举例一个很典型的操作,i++,这样的一个操作就不是线程安全的因为他,不是一个连贯操作,在实际运算时,被jvm编译成了几条jvm指令,这个时候可能一个线程加到一半还没有来得及赋值回去,这个时候CPU切换了另一个线程去执行,另一个线程接着上一次还没有来得及赋值之前的变量进行操作,这样就有一次操作浪费了,原子性就是要么不执行,要么全运行
像i++这种操作可以使用原子类去操作,使用下图中的AtomicInteger就行
在这里插入图片描述

其实解决这个问题最简单的方法还是加锁

锁知识概念

自旋锁:是指当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么改线程将循环等待,重试,直到获取锁,才会退出

乐观锁:有一种乐观的心态,他认为不会有数据发生冲突,操作是在修改数据是会去通过比对之前的版本,认为是否去更新数据

悲观锁:有一种悲观的心态,他认为会有数据发生冲突,去同步所有操作

独享锁(写):给资源加上写锁,其他线程不能在加锁(单写)
共享锁(多读):给资源加上读锁,只能读不能写入,其他线程也只能加,读锁,不能往这个资源上面加写锁

可重入锁:就是指可以重复加锁
不可重入锁:不能重复加锁

公平锁:按照循序执行
非公平锁:可以插队

锁消除:举例StringBuffer ,的append方法假如只在在单一的一个线程中去操作,没有涉及其他的一个线程抢的话,在多次执行之后JIT编译器会在运行时,帮我们锁消除也就是去掉同步关键字,开启锁消除的命令-XX:+DoEscapeAnalysis -XX:+Eliminatel ocks

锁粗化:每个同步代码块中,只有极小的一段代码,并且有多个同步代码块,在多次执行之后,JIT会帮我们编译成一个同步关键字

锁知识

synchronized 作用域:

synchronized 加在方法上面和某一段代码中(this)是,当前对象有效
synchronized 加在某一段代码中(类.class)或者static方法修饰的方法上面是,整个类有效

注意点:第一种情况可能在调用的时候,还是存在线程安装问题,当我们两个当前对象进行实例化,然后对数据库的同一个数据操作,这个时候就会有问题

synchronized 原理
在这里插入图片描述
synchronized 他的对象头有三块区域
1.Class Meta address 作用,在堆中指向方法区的引用
2.ArrayLength 作用,记录堆中数组的长度
3.Mark Work(重点)作用记录锁的状态,在这里插入图片描述

锁升级

Mrrk word : 无锁,偏向锁,轻量级锁,重量级锁

升级成偏向锁:首先是个无锁状态,这个时候进来了一个线程,升级成偏向锁,把自己的一个id修改成当前线程的ID,偏向锁作用在只有一个线程的时候,避免CAS不必要的自旋,即使代码执行完了,也不会退出下次来了也会直接进去执行。

升级成轻量级锁的过程:在虚拟机的栈帧中开辟一个叫lock record 的空间,然后把,当前锁的状态从Mark Word中拷贝过来,拷过来之后通过CAS去修改Mark Word 中的锁状态,在修改状态的(升级的时候)会有一个object monitor 的管程(监视器),在这里里面有个owner属性代表抢锁成功的对象,而抢锁失败的对象都会丢在EntryList中阻塞。

升级成重量级锁的过程:和上面几乎一样,把CAS抢锁模式变成,完全同步的模式

在线程通信时使用:wait / notify
wait : owner 把当前线程,放到waitSet中去状态变为等待,然后这时owner空出来了就触发抢锁操作,

notify:nwner中的线程告诉,waitSet中的线程,我执行完了,你可以执行了,owner当前线程去到exitMonitor里面去,然后EntryList中的线程就可以开始抢锁了

锁升级过程:首先是个无锁状态,这个时候进来了一个线程,升级成偏向锁,把自己的一个id修改成当前线程的ID,偏向锁作用在只有一个线程的时候,避免CAS不必要的自旋,即使代码执行完了,也不会退出下次来了也会直接进去执行。然后当有另一个线程来了,这个时候会发生争抢,锁会去升级成一个CAS锁,具体过程参考上面,当自旋到一定次数会还没有抢到锁,会升级成,重量级锁,具体过程参考上面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值