一个线程对主内存的修改可以及时的被其他线程观察到
线程交叉执行
没有在工作内存与主存间及时更新
JMM关于synchronized的规定
解锁前
,必须把共享变量的最新值刷新到主内存
加锁时
,将清空工作内存
中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值
(加锁与解锁是同一把锁
)通过加入内存屏障
和禁止指令重排序优化
来实现
写操作
时,会在写操作后
加入一条store屏障指令
,将本地内存中的共享变量值刷新到主内存
读操作
时,会在读操作前
加入一条load屏障指令
,从主内存中读取共享变量
用volatile做计数操作,看是否是线程安全的
package com.mmall.concurrency.example.count;
import com.mmall.concurrency.annoations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
@NotThreadSafe
public class CountExample4 {
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
public static volatile int count = 0;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count);
/*
运行结果:
5000
4992
4998
因此volatile也是线程不安全的
原因是:假设开始时count=5,线程1和线程2同时做count++操作时,线程1和线程2都从内存中读取到了count=5,然后线程1和线程2同时对count做加一操作,然后线程1把count的结果6由工作内存存回内存,线程2也把count的结果6由工作内存存回内存,因此运行结果小于等于5000
*/
}
private static void add() {
count++;
// 1、count
// 2、+1
// 3、count
}
}
运行结果:
5000
4992
4998
因此volatile也是线程不安全的
。
原因是:假设开始时count=5,线程1和线程2同时做count++操作时,线程1和线程2都从内存中读取到了count=5,然后线程1和线程2同时对count做加一操作,然后线程1把count的结果6由工作内存存回内存,线程2也把count的结果6由工作内存存回内存,因此运行结果小于等于5000。
volatile适合使用在状态标识
的场景中,如下实例:(volatile还可以用于检查2次的场景中)
volatile boolean inited = false;//全局变量
//线程1:
context = loadContext();
inited= true;
// 线程2:
while( !inited ){
sleep();
}
doSomethingWithConfig(context)
import java.util.Date;
/**
* Volatile不是坑
*
* @author wuyidi
* @version v2.0.1 2018年07月05日 10:33 wuyidi
*/
public class VolatileTestSample {
/**
* 非volatile变量,当前线程如果不刷新句柄,则永远不可见
* 也就是说,o的句柄一直是在Cache中
*/
//private Object o = null;
/**
* 当变量为volatile时,另一根线程对其改动,会立即可见
*/
private volatile Object o = null;
private boolean flag = false;
public void methodA() {
while (true) {
if (o != null && flag == false) {
System.out.println("Handler o detected changed."+new Date());
flag = true;
System.exit(0);
}
}
}
public void methodB() {
o = new Object();
System.out.println("Handler o changed!"+new Date());
}
public static void main(String[] args) {
System.out.println("当前时间是:"+new Date());
VolatileTestSample volatileTestSample = new VolatileTestSample();
Thread threadHandlerListener = new Thread(new Runnable() {
@Override
public void run() {
volatileTestSample.methodA();
}
});
threadHandlerListener.start();
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
volatileTestSample.methodB();
}
}
下面内容转自:
https://www.jianshu.com/p/895950290179
代码块
:作用区域是调用方法的对象 synchronized (this)
,不同对象之间互不影响
。方法
:作用区域是调用方法的对象
,不同对象之间互不影响
类synchronized (SynchronizedTest2.class)
:作用区域是是所有对象
静态方法
:作用区域是是所有对象
代码示例
Synchronize修饰一个代码块和修饰一个方法,表现一样
package com.mmall.concurrency.example.sync;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
/*
Synchronize修饰一个代码块和修饰一个方法,表现一样
* */
public class Synchronized Test1 {
// 修饰一个代码块:作用区域是调用方法的对象,不同对象之间互不影响
public synchronized void test1(int j) {
for (int i = 0; i < 10; i++) {
System.out.println("test1() " + j + " " + i);
}
}
// 修饰一个方法:作用区域是调用方法的对象,不同对象之间互不影响
// 如果此类是父类,子类要调用父类的时候,是不包含synchronized的,原因是synchronized不是方法声明的一部分。 如果子类也需要synchronized的话,需要声明synchronized。
public void test2(int j) {
synchronized (this) {
for (int i = 0; i < 10; i++) {
System.out.println("test2() " + j + " " + i);
}
}
}
public static void main(String[] args) {
SynchronizedTest1 example1 = new SynchronizedTest1();
SynchronizedTest1 example2 = new SynchronizedTest1();
ExecutorService executorService = Executors.newCachedThreadPool();
//2个对象调用方法1
executorService.execute(() -> {
// 运行结果是:test1 1和test1 2从0-9交叉出现
example1.test1(1);
});
executorService.execute(() -> {
example1.test1(2);
});
//2个对象调用方法2
executorService.execute(() -> {
// 运行结果也是:test1 1和test1 2从0-9交叉出现
example1.test2(1);
});
executorService.execute(() -> {
example2.test2(2);
});
}
}
运行结果:test1 1和test1 2从0-9交叉出现
test2() 1 0
test1() 2 0
test2() 1 1
test2() 1 2
test1() 2 1
test2() 1 3
test1() 2 2
test2() 1 4
test1() 2 3
test1() 2 4
test2() 1 5
test1() 2 5
test2() 1 6
test1() 2 6
test2() 1 7
test1() 2 7
test2() 1 8
test1() 2 8
test2() 1 9
test1() 2 9
test2() 2 0
test2() 2 1
test2() 2 2
test2() 2 3
test2() 2 4
test2() 2 5
test2() 2 6
test2() 2 7
test2() 2 8
test2() 2 9
test1() 1 0
test1() 1 1
test1() 1 2
test1() 1 3
test1() 1 4
test1() 1 5
test1() 1 6
test1() 1 7
test1() 1 8
test1() 1 9
Synchronize修饰一个类
和修饰一个静态方法
,表现一样
package com.mmall.concurrency.example.sync;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
/*
Synchronize修饰一个类和修饰一个静态方法,表现一样
* */
public class SynchronizedTest2 {
// 修饰一个类:作用区域是是所有对象
public static void test1(int j) {
synchronized (SynchronizedTest2.class) {
for (int i = 0; i < 10; i++) {
System.out.println("test1() " + j + " " + i);
}
}
}
// 修饰一个静态方法:作用区域是是所有对象
public static synchronized void test2(int j) {
for (int i = 0; i < 10; i++) {
System.out.println("test2() " + j + " " + i);
}
}
public static void main(String[] args) {
SynchronizedTest2 example1 = new SynchronizedTest2();
SynchronizedTest2 example2 = new SynchronizedTest2();
ExecutorService executorService = Executors.newCachedThreadPool();
//2个对象调用方法2
// executorService.execute(() -> {
// // 运行结果是:test1 1和test1 2分别从0-9出现(不交叉)
// example1.test2(1);
// });
// executorService.execute(() -> {
// example2.test2(2);
// });
//2个对象调用方法1
executorService.execute(() -> {
// 运行结果也是:test1 1和test1 2分别从0-9出现(不交叉)
example1.test1(1);
});
executorService.execute(() -> {
example2.test1(2);
});
}
}
运行结果:test1 1和test1 2分别从0-9出现(不交叉)
test1() 1 0
test1() 1 1
test1() 1 2
test1() 1 3
test1() 1 4
test1() 1 5
test1() 1 6
test1() 1 7
test1() 1 8
test1() 1 9
test1() 2 0
test1() 2 1
test1() 2 2
test1() 2 3
test1() 2 4
test1() 2 5
test1() 2 6
test1() 2 7
test1() 2 8
test1() 2 9
Synchronize修饰一个方法时,如果此类是父类,子类要调用父类的时候,是不包含synchronized的,原因是synchronized不是方法声明的一部分。 如果子类也需要synchronized的话,需要声明synchronized。