1.volatile是java虚拟机提供的轻量级的同步机制
- 保证可见性
package register;
import java.util.concurrent.TimeUnit;
class MyData{
volatile int number=0;
public void addTo60(){
this.number=60;
}
}
/**
* 1.验证volatile可见性
* 1.1 假如int number=0;number变量之前没有添加volatile关键字修饰,没有可见性
*/
public class VolatileDemo {
public static void main(String[] args) {
MyData myData = new MyData();
new Thread( () -> {
try{
System.out.println(Thread.currentThread().getName()+"\t come in");
TimeUnit.SECONDS.sleep(3);
}catch (Exception e){
e.printStackTrace();;
}
myData.addTo60();
System.out.println(Thread.currentThread().getName()+"\t update value :"+myData.number);
},"AAA").start();
//第二个线程是main函数
while (myData.number==0){
}
//没有volatile修饰,线程卡死,AAA线程修改了number!=0,main线程不可见,一直循环
System.out.println(Thread.currentThread().getName()+"\t cmiss is over");
}
}
- 不保证原子性
package register;
import java.util.concurrent.TimeUnit;
class MyData{
volatile int number=0;
public void addTo60(){
this.number=60;
}
public void addPublsPubls(){
number++;
}
}
/**
* 1.验证volatile可见性
* 1.1 假如int number=0;number变量之前没有添加volatile关键字修饰,没有可见性
* 1.2 添加volatile可以解决可见性问题
*
* 2.验证volatile不保证原子性
* 2.1 原子性是什么意思?
* 不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者分割。需要整体完整。
* 要么同时成功,要么同时失败
*/
public class VolatileDemo {
public static void main(String[] args) {
MyData myData = new MyData();
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j <1000 ; j++) {
myData.addPublsPubls();
}
},String.valueOf(i)).start();
}
//需要等待上面20个线程全部计算完成,再用main线程取得最终的结果值看是多少
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println("final value is:\t"+myData.number);
}
}
造成少于20000原因
如何解决
package register;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class MyData{
volatile int number=0;
public void addTo60(){
this.number=60;
}
public void addPublsPubls(){
number++;
}
//解决volatile不保证原子性问题
AtomicInteger atomicInteger = new AtomicInteger();
public void addMyAtomic(){
atomicInteger.getAndIncrement();
}
}
/**
* 1.验证volatile可见性
* 1.1 假如int number=0;number变量之前没有添加volatile关键字修饰,没有可见性
* 1.2 添加volatile可以解决可见性问题
*
* 2.验证volatile不保证原子性
* 2.1 原子性是什么意思?
* 不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者分割。需要整体完整。
* 要么同时成功,要么同时失败
* 2.2 why
*
* 3.3 怎么解决 JUC
*/
public class VolatileDemo {
public static void main(String[] args) {
MyData myData = new MyData();
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j <1000 ; j++) {
myData.addPublsPubls();
myData.addMyAtomic();
}
},String.valueOf(i)).start();
}
//需要等待上面20个线程全部计算完成,再用main线程取得最终的结果值看是多少
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println("final value is:\t"+myData.number);
System.out.println("Atomic final value is:\t"+myData.atomicInteger);
}
}
- 禁止指令重拍
2.谈谈JMM
- JMM--java内存模型
JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。
JMM关于同步的规定:
1).线程解锁前,必须把共享变量的值刷新回主内存
2).线程加锁前,必须读取主内存的最新值到自己的工作内存
3).加锁解锁是同一把锁
由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),工.作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存中的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下图:
- JMM--特性
(1)可见性
t1线程修改了主物理内存的值,马上通知其他线程,其他线程取的值作废,重新从内存中获取最新值。
(2)原子性
(3)有序性
3.你在那些地方用到过volatile
3.1单例模式DCL代码
package register;
public class SingletonDemo {
private static SingletonDemo instance=null;
private SingletonDemo(){
System.out.println(Thread.currentThread().getName()+"\t 我是构造方法SingletonDemo()");
}
//多线程模式下可以在方法上使用synchronized进行修饰,可以保证单利模式下只创建一个实例。
// 但是synchronized会锁住整个方法,使效率变低
public static SingletonDemo getInstance(){
if(instance==null){
instance=new SingletonDemo();
}
return instance;
}
public static void main(String[] args) {
//单线程(main函数的操作动作)
//System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());
//System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());
//System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());
//多线程
for (int i = 0; i <10 ; i++) {
new Thread(()->{
SingletonDemo.getInstance();
},String.valueOf(i)).start();
}
}
}
DCL模式:Double check lock双端检索机制,在获取单例方法内使用synchronized代码块及双null判断,DCL机制不一定线程安全,原因是会存在有指令重排的存在,加入volatile可以进制指令重排
public static SingletonDemo getInstance(){//方法
if(instance==null){
synchronized (SingletonDemo.class){
if(instance==null){
instance=new SingletonDemo();
}
}
}
return instance;
}
3.2 单例模式volatile分析
package register;
public class SingletonDemo {
//使用volatile修饰,可以有效解决指令重排造成多线程下单利模式获得多个实例被创建
private volatile static SingletonDemo instance=null;
private SingletonDemo(){
System.out.println(Thread.currentThread().getName()+"\t 我是构造方法SingletonDemo()");
}
public static SingletonDemo getInstance(){
if(instance==null){
synchronized (SingletonDemo.class){
if(instance==null){
instance=new SingletonDemo();
}
}
}
return instance;
}
public static void main(String[] args) {
//单线程(main函数的操作动作)
//System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());
//System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());
//System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());
//多线程
for (int i = 0; i <10 ; i++) {
new Thread(()->{
SingletonDemo.getInstance();
},String.valueOf(i)).start();
}
}
}