java内存模型(jmm)
-
java内存模型分为两大类型即,主内存和本地内存
-
1.主内存
-
也就是主进程所占用的内存
-
2.本地内存
-
线程中开辟的属于线程自己的内存,其中存放着全局变量的副本数据
-
也就是全局共享的数据实在主内存中的,线程中复制一份放入自己的本地内存,线程执行结束后将其变动刷新至主内存。
-
这也就是为什么多线程会有线程安全的问题所在。
-
多个线程同时做了修改,都去刷新主内存,会造成结果和实际不一致。
volatile关键字
-
其功能为多线程可见。
-
怎么说呢?实际上就是当线程在本地内存中修改了被
volatile修改的变量后,会立刻将其值刷新至主内存中,其他线程
-
也会马上得到这个修改的结果
-
实际看上去就像线程在直接修改主线程中的这个变量一样。
-
-
还有就是防止指令重新排序
-
代码自上而下执行,这是常识,但是,计算机执行时,可能会将,没有依赖关系的代码指令的执行顺序打乱,但一般不会
-
影响结果。
-
如:
int a=
1;
int b=
1;这时无论怎么变化这两条指令,都没有影响。
-
如果再来一条
int c=a+b;就会相互之间有了依赖关系,计算机就不会将其重新排序
-
-
而对于那些比较隐晦的,指令重拍可能会引发问题得数据,
volatile关键字可以指定其对应的代码不会参与重新排序
案例
-
package live.yanxiaohui;
-
-
/**
-
* @Description 仿真消息的生产和消费(多线程之间的通讯),即来一个消息,消费一条消息
-
* @CSDN https://blog.csdn.net/yxh13521338301
-
* @Author: yanxh<br>
-
* @Date 2020-05-15 11:08<br>
-
* @Version 1.0<br>
-
*/
-
public
class Test2 {
-
public static void main(String[] args) {
-
Money money =
new Money();
-
new Task1(money).start();
-
new Task2(money).start();
-
}
-
}
-
-
class Task1 extends Thread{
-
private Money money;
-
-
private
int count;
-
public Task1(Money money) {
-
this.money = money;
-
}
-
-
@Override
-
public void run() {
-
while (
true){
-
if(count %
2 ==
0){
-
money.name =
"马云";
-
money.componet =
"阿里";
-
}
else {
-
money.name =
"马化腾";
-
money.componet =
"腾讯";
-
}
-
count++;
-
}
-
}
-
}
-
-
class Task2 extends Thread{
-
private Money money;
-
-
public Task2(Money money) {
-
this.money = money;
-
}
-
-
@Override
-
public void run() {
-
while (
true){
-
System.out.println(money.name +
"," + money.componet);
-
}
-
}
-
}
-
-
class Money{
-
public String name;
-
public String componet;
-
}
首先我们运行程序,会发现数据错乱的现象
-
这是由于多个线程访问全局共享数据造成的线程安全问题。
-
说人话:
-
写入的线程做了修改,将本地内存改动的数据要同步至主内存,刚好同步一半,这时被挂起,
-
读线程从主内存读取数据就是一个错误的数据。
那么加锁如何?
-
对共享的money对象加锁。关键代码如下
-
写入操作:
-
synchronized (money){
-
if(count %
2 ==
0){
-
money.name =
"马云";
-
money.componet =
"阿里";
-
}
else {
-
money.name =
"马化腾";
-
money.componet =
"腾讯";
-
}
-
}
-
-
读操作:
-
synchronized (money){
-
System.out.println(money.name +
"," + money.componet);
-
}
一定会有读者有疑问:读为什么还要加锁?
-
此处对数据的修改和读取是两个变量,这两个变量需要保证其原子性,否则依旧是可能出现线程安全的
-
比如:如果读不加锁,那么其读到第一个属性值后,被挂起,写入的线程做了修改,读线程继续执行后
-
就会出现第二个属性和第一个属性不同步的现象
运行程序
数据是保证一致性了,但依然不满足我们消费的机制,所以需要再优化下
-
我们可以使用wait和notify配合进行多个线程之间对同一共享的全局数据进行通讯
-
-
-
package live.yanxiaohui;
-
-
/**
-
* @Description 仿真消息的生产和消费(多线程之间的通讯),即来一个消息,消费一条消息
-
* @CSDN https://blog.csdn.net/yxh13521338301
-
* @Author: yanxh<br>
-
* @Date 2020-05-15 11:08<br>
-
* @Version 1.0<br>
-
*/
-
public
class Test2 {
-
public static void main(String[] args) {
-
Money money =
new Money();
-
new Task1(money).start();
-
new Task2(money).start();
-
}
-
}
-
-
class Task1 extends Thread{
-
private Money money;
-
-
private
int count;
-
-
-
-
public Task1(Money money) {
-
this.money = money;
-
}
-
-
@Override
-
public void run() {
-
try{
-
while (
true){
-
synchronized (money){
-
// 方便查看效果,设置阻塞时间
-
Thread.sleep(
100);
-
if(!money.success){
-
// 交出对象的使用权,等待消息消费
-
money.wait();
-
}
-
if(count %
2 ==
0){
-
money.name =
"马云";
-
money.componet =
"阿里";
-
}
else {
-
money.name =
"马化腾";
-
money.componet =
"腾讯";
-
}
-
money.success =
false;
-
// 唤醒此对象所有等待的线程
-
money.notify();
-
}
-
count++;
-
}
-
}
catch (Exception e){
-
e.printStackTrace();
-
}
-
}
-
}
-
-
class Task2 extends Thread{
-
private Money money;
-
-
public Task2(Money money) {
-
this.money = money;
-
}
-
-
@Override
-
public void run() {
-
try {
-
while (
true){
-
synchronized (money){
-
if(money.success){
-
// 交出对象的使用权,等待消息生产
-
money.wait();
-
}
-
System.out.println(money.name +
"," + money.componet);
-
money.success =
true;
-
// 唤醒此对象所有等待的线程
-
money.notify();
-
}
-
}
-
}
catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
-
class Money{
-
public String name;
-
public String componet;
-
// true表示可以写,false表示可以读
-
public
boolean success;
-
}
由此可以看出,线程执行是抢夺CPU分配权的,和主线程代码的执行顺序没太大关系
我们可以使用lock锁,去替代sync
-
package live.yanxiaohui;
-
-
import java.util.concurrent.locks.Condition;
-
import java.util.concurrent.locks.Lock;
-
import java.util.concurrent.locks.ReentrantLock;
-
-
/**
-
* @Description 仿真消息的生产和消费(多线程之间的通讯),即来一个消息,消费一条消息
-
* @CSDN https://blog.csdn.net/yxh13521338301
-
* @Author: yanxh<br>
-
* @Date 2020-05-15 11:08<br>
-
* @Version 1.0<br>
-
*/
-
public
class Test3 {
-
public static void main(String[] args) {
-
Money money =
new Money();
-
new Task1(money).start();
-
new Task2(money).start();
-
}
-
-
-
}
-
-
class Task1 extends Thread {
-
private Money money;
-
-
private
int count;
-
-
-
public Task1(Money money) {
-
this.money = money;
-
}
-
-
@Override
-
public void run() {
-
while (
true) {
-
try {
-
// 对象上锁
-
money.lock.lock();
-
// 方便查看效果,设置阻塞时间
-
Thread.sleep(
100);
-
if (!money.success) {
-
// 交出对象的使用权,等待消息消费
-
money.condition.await();
-
}
-
if (count %
2 ==
0) {
-
money.name =
"马云";
-
money.componet =
"阿里";
-
}
else {
-
money.name =
"马化腾";
-
money.componet =
"腾讯";
-
}
-
money.success =
false;
-
// 唤醒此对象所有等待的线程
-
money.condition.signal();
-
count++;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
}
finally {
-
// 释放锁
-
money.lock.unlock();
-
}
-
}
-
}
-
}
-
-
class Task2 extends Thread {
-
private Money money;
-
-
public Task2(Money money) {
-
this.money = money;
-
}
-
-
@Override
-
public void run() {
-
try {
-
while (
true) {
-
// 对象上锁
-
money.lock.lock();
-
if (money.success) {
-
// 交出对象的使用权,等待消息生产
-
money.condition.await();
-
}
-
System.out.println(money.name +
"," + money.componet);
-
money.success =
true;
-
// 唤醒此对象所有等待的线程
-
money.condition.signal();
-
}
-
}
catch (InterruptedException e) {
-
e.printStackTrace();
-
}
finally {
-
// 释放锁
-
money.lock.unlock();
-
}
-
}
-
}
-
-
class Money {
-
public String name;
-
public String componet;
-
// true表示可以写,false表示可以读
-
public
boolean success;
-
-
public Lock lock =
new ReentrantLock();
-
-
public Condition condition = lock.newCondition();
-
}
-
运行结果也是相同
总结
-
1.多线程之间实现通讯基于对全局共享数据的锁定,即多个线程都必须去争夺锁,获取锁之后依照业务进行对应的等待或运行。
-
2.wait和notify必须要配合
synchronized使用, 且必须要在同步代码块中执行
-
3.lock锁因为是手动添加的,所以它可控,实际开发中可用到的最多
-
4.wait底层是将对象的锁释放掉,自身线程进入阻塞等待状态,直到被对象的notify唤醒
-
5.sleep只是线程的定时阻塞,不会释放锁
-
6.
volatile指定全局共享变量在内存模型中线程间数据的可见性
-
7.
volatile还可以防止指令重新排序
转载:https://blog.csdn.net/yxh13521338301/article/details/106137976
查看评论