十八、volatile
1、保证内存可见性
package chapter04;
import java.util.concurrent.TimeUnit;
/**
* @Author Noperx
* @Create 2021-02-28 23:43
*/
public class JUCTest18 {
static volatile int i = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{while (i==0){ } }).start();
TimeUnit.SECONDS.sleep(1);
i = 1;
System.out.println(i);
}
}
1614527653296.png
1614527613975.png
2、不保证原子性,需使用原子类
package chapter04;
import java.util.concurrent.TimeUnit;
/**
* @Author Noperx
* @Create 2021-02-28 23:43
*/
public class JUCTest18 {
static int i = 0;
public static void add(){
i++;
}
public static void main(String[] args) throws InterruptedException {
for (int j = 0; j < 10; j++) {
new Thread(()->{
for (int k = 0; k < 1000; k++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){//这里就是让出main线程和gc线程,去执行i++
Thread.yield();//使当前线程由执行状态,变成为就绪状态,让出cpu时间,在下一个线程执行时候,此线程有可能被执行,也有可能没有被执行。
}
System.out.println(i);
}
}
1614528583901.png
1614528926398.png
3、禁止指令重排
原理:内存屏障
十九、单例模式
1、饿汉式
package chapter04.singleton;
/**
* @Author Noperx
* @Create 2021-03-01 20:52
*/
public class SingletonTest {
private SingletonTest(){
}
private static SingletonTest singletonTest = new SingletonTest();
public static SingletonTest getSingletonTest(){
return singletonTest;
}
}
2、DCL懒汉式
由于反射的存在,还是可以创建多例
package chapter04.singleton;
/**
* @Author Noperx
* @Create 2021-03-01 20:52
*/
public class SingletonTest02 {
private SingletonTest02(){
System.out.println("initiate just once");
}
private static volatile SingletonTest02 singletonTest02;
public static SingletonTest02 getSingletonTest(){
if(singletonTest02==null){
synchronized (SingletonTest02.class){
if(singletonTest02==null){
/*
1、初始化内存空间
2、创建对象
3、将对象执行内存空间
*/
singletonTest02 = new SingletonTest02();//不是一个原子性操作,可能发生指令重排,所以必须使用volatile关键字
return singletonTest02;
}
}
}
return singletonTest02;
}
}
package chapter04;
import chapter04.singleton.SingletonTest;
import chapter04.singleton.SingletonTest02;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @Author Noperx
* @Create 2021-03-01 20:51
*/
public class JUCTest19 {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
System.out.println(SingletonTest02.getSingletonTest());
Constructor<SingletonTest02> declaredConstructor = SingletonTest02.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
System.out.println(declaredConstructor.newInstance());
}
}
1614606697343.png
3、静态内部类式
package chapter04.singleton;
import com.sun.org.apache.bcel.internal.generic.NEW;
/**
* @Author Noperx
* @Create 2021-03-01 20:52
*/
public class SingletonTest03 {
private SingletonTest03(){
}
private static SingletonTest03 getInstance(){
return InnerClass.singletonTest03;
};
static class InnerClass{
private final static SingletonTest03 singletonTest03= new SingletonTest03();
}
}
4、枚举式
package chapter04.singleton;
/**
* @Author Noperx
* @Create 2021-03-01 20:52
*/
public enum SingletonTest04 {
INSTANCE;
public static SingletonTest04 getInstance(){
return INSTANCE;
}
}
1614607179024.png
枚举类并没有无参构造函数,以下假的
1614607475761.png
1614608282109.png
二十、CAS
Unsafe类:可以通过它操作计算机内存
1614612162048.png
CAS:比较当前内存中的值和主内存中的值,一致更新该值,否则不执行
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
ABA问题
package chapter04;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author Noperx
* @Create 2021-03-01 23:10
*/
public class JUCTest20 {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger();
//假设这是个线程
atomicInteger.compareAndSet(0,1);
atomicInteger.compareAndSet(1,0);
System.out.println(atomicInteger.get());
//假设这是另个线程
atomicInteger.compareAndSet(0,100);
System.out.println(atomicInteger.get());
}
}
解决办法--带时间戳,乐观锁原理
package chapter04;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* @Author Noperx
* @Create 2021-03-01 23:10
*/
public class JUCTest20 {
public static void main(String[] args) {
AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(1, 1);
new Thread(()->{
System.out.println("a1->"+stampedReference.getStamp());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(stampedReference.compareAndSet(1, 2, stampedReference.getStamp(), stampedReference.getStamp() + 1));
System.out.println("a2->"+stampedReference.getStamp());
System.out.println(stampedReference.compareAndSet(2, 1, stampedReference.getStamp(), stampedReference.getStamp() + 1));
System.out.println("a3->"+stampedReference.getStamp());
},"A").start();
new Thread(()->{
int temp = stampedReference.getStamp();
System.out.println("b1->"+stampedReference.getStamp());
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(stampedReference.compareAndSet(1, 20, temp, temp + 1));
System.out.println("b2->"+stampedReference.getReference());
},"B").start();
}
}
a1->1
b1->1
true
a2->2
true
a3->3
false
b2->1
二十一、几种锁
1、可重入锁
相当于手机解锁后你可以访问任何内容。
package chapter04;
/**
* @Author Noperx
* @Create 2021-02-27 21:38
*/
public class JUCTest21 {
public static void main(String[] args) throws InterruptedException {
Person03 person = new Person03();
new Thread(()->{ person.buyCar(); }).start();
new Thread(()->{ person.buyHouse();}).start();
}
}
class Person03{
public synchronized void buyCar() {
System.out.println(Thread.currentThread().getName()+"买车");
buyHouse();
}
public synchronized void buyHouse(){
System.out.println(Thread.currentThread().getName()+"买房");
}
}
Thread-0买车
Thread-0买房
Thread-1买房
2、自旋锁
不断尝试直到成功
package chapter04;
import java.util.concurrent.atomic.AtomicReference;
/**
* @Author Noperx
* @Create 2021-03-02 23:03
*/
public class MyLock {
AtomicReference<Thread> stampedReference = new AtomicReference();
public void lock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"->lock");
while (!stampedReference.compareAndSet(null,thread)){
}
}
public void unlock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"->unlock");
stampedReference.compareAndSet(thread,null);
}
}
package chapter04;
import java.util.concurrent.TimeUnit;
/**
* @Author Noperx
* @Create 2021-03-02 22:40
*/
public class Phone {
MyLock myLock = new MyLock();
public void call() {
myLock.lock();
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"call");
} catch (Exception e) {
e.printStackTrace();
} finally {
myLock.unlock();
}
}
public void smg() {
myLock.lock();
try {
System.out.println(Thread.currentThread().getName()+"smg");
} catch (Exception e) {
e.printStackTrace();
} finally {
myLock.unlock();
}
}
}
package chapter04;
import java.util.concurrent.TimeUnit;
/**
* @Author Noperx
* @Create 2021-03-02 23:02
*/
public class MyLockTest {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
phone.call();
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
phone.smg();
},"B").start();
}
}
A->lock
B->lock
Acall
A->unlock
Bsmg
B->unlock