内存模型:
1、硬件内存模型:
在多个CPU同时访问和修改同一数据时,会遵循CPU的缓存一致性原则。
从一开始的硬件内存模型,再到后面的程序语言的发展,一只发展到语言层面的内存模型。让程序语言有自己的专属内存模型。
2、Java内存模型:
线程通过计算机指令对内存数据进行读写等操作!
基本的指令:
1.主内存:lock(锁定)、unlock(解锁)、write(写入)、read(读取)
2.工作内存:load(加载)、use(使用)、assign(赋值)、store(存储)
具体步骤:
在线程A将x=1的共享变量加载进内存,先将x标记为独占状态,即加锁lock,然后assign和store对x=1进行赋值和存储操作,将x在内存中由1修改为2,存储到内存中,然后通过write将数据写进主存中,最后去释放x的锁定状态unlock。
在A对数据x进行操作完,B线程开始对x进行操作一系列操作。
上述的步骤是在多线程的理想情况下的数据操作方式,在实际情况下,一般都是同时读取x的副本到缓存中,在线程A修改完x的值后,B的缓存中x的值还是之前读取的x的副本x=1,所以对后面的操作来说,违背了数据的可见性问题和原子性问题。
在线程操作数据时一般会出现上述的问题:
可见性问题:在A线程操作完数据之后,要让其他线程知道被修改的数据的相关操作。详细来说就是在线程A操作完数据后,将数据重新 刷新进内存。
``
package com.jmh.ppp;
/*
PACKAGE_NAME:com.jmh.ppp
USER:18413
DATE:2021/10/8 9:51
PROJECT_NAME:RabbitMq
面向代码面向君,不负代码不负卿————蒋明辉 */
public class TestJVM {
static int a=1;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
while(a!=2){
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
a=2;
}
});
thread1.start();
Thread.sleep(1000);
thread2.start();
}
}
上面的代码问题,在线程1执行时,线程1会一直执行下去,线程休眠后,线程2开始执行,将a的值修改为2,此时,线程1并不会跳出循环而是会一直执行下去。
解决方案1:使用volataile关键字修饰变量,修饰完的变量总是会主动加载进主内存。
解决方案2:使用synchronized同步,底层去使用lock和unlock方法去对数据进行操作。
``
package com.jmh.ppp;
/*
PACKAGE_NAME:com.jmh.ppp
USER:18413
DATE:2021/10/8 9:51
PROJECT_NAME:RabbitMq
面向代码面向君,不负代码不负卿————蒋明辉 */
public class TestJVM {
static int a=1;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
while(a!=2){
synchronized (this){
int b=a+1;
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
a=2;
}
});
thread1.start();
Thread.sleep(1000);
thread2.start();
}
}
也有可能是CPU的指令重排,导致的程序的顺序执行问题。
解决方案1:volataile关键字,禁止指令重排。
解决方案2:synchronize线程同步。
package com.jmh.ppp;
/*
PACKAGE_NAME:com.jmh.ppp
USER:18413
DATE:2021/10/8 9:51
PROJECT_NAME:RabbitMq
面向代码面向君,不负代码不负卿————蒋明辉 */
public class TestJVM {
static int a=1;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
while(a!=2){
synchronized (this){
int b=a+1;
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
a=2;
}
});
thread1.start();
Thread.sleep(1000);
thread2.start();
}
}
这里面就得提到happens-before原则:对于两个操作A和B,这两个操作是在不同的线程中执行的,如果线程A happens-before 线程B(A优先于B执行),name可以保证,A的操作相对B的操作时可见的。下面是happens-before原则的几个规则(加粗表示重点掌握):
1.程序顺序规则:无论是否重排,与顺序执行的结果一致
2.锁定规则:对一个锁的解锁,happens-before这个锁的加锁
package com.jmh.ppp;
/*
PACKAGE_NAME:com.jmh.ppp
USER:18413
DATE:2021/10/8 10:48
PROJECT_NAME:RabbitMq
面向代码面向君,不负代码不负卿————蒋明辉 */
public class Test01 {
private int value=1;
public synchronized void setValue(int value){
this.value=value;
}
public synchronized int getValue(){
return value;
}
}
3.volatile变量规则
4.线程启动规则
5.线程结束规则
6.中断规则
7.终结器规则
8.传递性规则
有序性问题:
原子性问题:一个操作时不可中断的,要么全部成功,要么全部失败。