意思是可变的
作用:
- 保证线程可见性(每次写都会被线程读到)
- MESI
- 缓存一致性协议
- 禁止指令重排序(CPU)
- DCL单例
- Double Check Lock
package singleton;
public class singleton01 {
private static final singleton01 INSTANCE = new singleton01();
private singleton01(){};
public static singleton01 getInstance(){
return INSTANCE;
}
public void m(){
System.out.println("m");
}
public static void main(String[] args) {
singleton01 s1 = singleton01.getInstance();
singleton01 s2 = singleton01.getInstance();
System.out.println(s1 == s2);
}
}
懒汉式
package singleton;
public class singleton02 {
private static singleton02 INSTANCE;
private singleton02(){};
public static singleton02 getInstance(){
if (INSTANCE == null){
try{
Thread.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
INSTANCE = new singleton02();
}
return INSTANCE;
}
public void m(){
System.out.println("m");
}
public static void main(String[] args) {
for (int i = 0; i < 100;i++){
new Thread(()->{
System.out.println(singleton02.getInstance().hashCode());
}).start();
}
}
}
优化:双重检查
package singleton;
public class singleton03 {
private static singleton03 INSTANCE;
private singleton03(){};
public static singleton03 getInstance(){
if (INSTANCE == null) {
//双重检查
synchronized (singleton03.class) {
if (INSTANCE == null){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new singleton03();
}
}
}
return INSTANCE;
}
public void m(){
System.out.println("m");
}
public static void main(String[] args) {
for (int i = 0; i < 100;i++){
new Thread(()->{
System.out.println(singleton03.getInstance().hashCode());
}).start();
}
}
}
问题:双重检查需不需要加volatile?
要,因为不加会产生指令重排序,
对象初始化一半的情况下,instance对象有默认值,这时候另一个线程返回了instance,导致出现instance还是默认值的情况
不能!
package character02;
import java.util.ArrayList;
import java.util.List;
public class volatile02 {
volatile int count = 0;
void m(){
for (int i = 0; i < 100000; i++) {
count++;
}
}
public static void main(String[] args) {
volatile02 v = new volatile02();
List threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(v::m,"thread-"+i));
}
threads.forEach((o)->o.start());
threads.forEach((o)->{
try{
o.join();
}catch (InterruptedException e){
e.printStackTrace();
}
});
System.out.println(v.count);
}
}
锁优化
FineCoarseLock
同步代码块的语句越少越好
package character02;
import java.util.concurrent.TimeUnit;
public class Synchronzied {
private int count = 0;
synchronized void m1(){
try{
TimeUnit.SECONDS.sleep(2);
}catch (InterruptedException e){
e.printStackTrace();
}
count++;
try{
TimeUnit.SECONDS.sleep(2);
}catch (InterruptedException e){
e.printStackTrace();
}
}
void m2(){
try{
TimeUnit.SECONDS.sleep(2);
}catch (InterruptedException e){
e.printStackTrace();
}
//业务逻辑只有下面一句话需要sync,这时候不应该给整个方法上锁
//采用细粒度的锁,可以使线程争用时间变短,从而提高效率
synchronized(this) {
count++;
}
try{
TimeUnit.SECONDS.sleep(2);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
锁对象需加final修饰
package character02;
import java.util.concurrent.TimeUnit;
public class Synchronzied02 {
Object o = new Object();
void m(){
synchronized (o){
while (true){
try {
TimeUnit.SECONDS.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
Synchronzied02 s = new Synchronzied02();
//启动第一个线程
new Thread(s::m,"s1").start();
try {
TimeUnit.SECONDS.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
//创建第二个线程
Thread t2 = new Thread(s::m,"s2");
//锁对象发生改变,所以t2线程能够执行,如果注释掉,t2永远不能执行
s.o = new Object();
t2.start();
}
}
CAS(无锁优化 自旋)
Compare And Swap
cas(V,Expected,NewValue)
- if v == E
V = new
otherwise try again or fail - CPU原语支持
解释:如果v值等于期望(expected)的,则将v修改为newValue值
是cpu指令级别的操作,不能被打断
java.util.concurrent.atomic.*的类都是使用CAS来保证线程安全
package character02;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class CAS01 {
AtomicInteger count = new AtomicInteger(0);
void m(){
for (int i = 0; i < 10000; i++) {
count.incrementAndGet(); //count++
}
}
public static void main(String[] args) {
CAS01 c = new CAS01();
List threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(c::m,"thread-"+i));
}
threads.forEach((o)->o.start());
threads.forEach((o)->{
try{
o.join();
}catch (InterruptedException e){
e.printStackTrace();
}
});
System.out.println(c.count);
}
}
ABA问题
如果基本类型,无所谓
如果引用类型,就像你女朋友跟你复合,已经是别人的形状了
解释:
两个线程t1,t2;
t1执行cas(Object,1,2),t2先将Object改成了2,再改回1,这时候不影响t1的cas操作
解决:给Object加上一个版本号,修改一次版本号加一
可以使用AtomicStampedReference解决ABA问题
不能直接使用
功能:
- 直接操作内存
- 直接生成类实例
- 直接操作类或类变量
- CAS相关操作



