👨🏻🎓博主介绍:大家好,我是芝士味的椒盐,一名在校大学生,热爱分享知识,很高兴在这里认识大家🌟
🌈擅长领域:Java、大数据、运维、电子
🙏🏻如果本文章各位小伙伴们有帮助的话,🍭关注+👍🏻点赞+🗣评论+📦收藏,相应的有空了我也会回访,互助!!!
🤝另本人水平有限,旨在创作简单易懂的文章,在文章描述时如有错,恳请各位大佬指正,在此感谢!!!
@[TOC]
Volatile
保证可见性
package icu.lookyousmileface.volatilecode; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @author starrysky * @title: VolatileUse * @projectName Juc_Pro * @description: volatile原子性测试 * @date 2021/1/307:52 上午 */ public class VolatileShow { /** * // 不加 volatile 程序就会死循环! * // 加 volatile 可以保证可见性 */ private volatile static int num = 0; public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 5, Runtime.getRuntime().availableProcessors(), 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy() ); //线程1对内存中num=1的变化不知道 threadPoolExecutor.execute(() -> { while (num == 0) { } }); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } num = 1; System.out.println("num:"+num); threadPoolExecutor.shutdown(); } }
不保证原子性
原子性 : 不可分割
线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败。
/** * @author starrysky * @title: VolatileNotAtomic * @projectName Juc_Pro * @description: Volatile不保证原子性 * @date 2021/1/3011:32 上午 */ public class VolatileNotAtomic { private volatile static int num = 0; public static void add(){ num++; } public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 5, Runtime.getRuntime().availableProcessors(), 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() ); try { for (int i = 1; i <= 20; i++) { threadPoolExecutor.execute(() -> { for (int j = 1; j <= 1000; j++) { add(); } }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPoolExecutor.shutdown(); } Thread.currentThread().getThreadGroup().list(); System.out.println("sum:" + num); } }
为了保证原子性,不实用sync、lock,可以使用atomic包下的原子类
/** * @author starrysky * @title: AtomicUse * @projectName Juc_Pro * @description: 原子类使用 * @date 2021/1/3011:51 上午 */ public class AtomicUse { private volatile static AtomicInteger number = new AtomicInteger(); public static void add(){ number.getAndIncrement(); } public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 5, Runtime.getRuntime().availableProcessors(), 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() ); try { for (int i = 1; i <= 20; i++) { threadPoolExecutor.execute(()->{ for (int j = 1; j <= 1000; j++) { add(); } }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPoolExecutor.shutdown(); } /** * 让出主线程的CPU时间片,让其他线程使用 */ while (Thread.activeCount()>2){ System.out.println(Thread.currentThread().getName()); Thread.yield(); } System.out.println("result:"+number); } }
- 禁止指令重排
什么是 指令重排:你写的程序,计算机并不是按照你写的那样去执行的。
源代码-->编译器优化的重排--> 指令并行也可能会重排--> 内存系统也会重排---> 执行
处理器在进行指令重排的时候,考虑:数据之间的依赖性!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oVkWRf6K-1644224594265)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/acc5d510-aa44-4c78-bcba-854975117af7/Untitled.png)]
单例模式
饿汉 单例模式
/** * @author starrysky * @title: Hungry * @projectName Juc_Pro * @description: 饿汉 单例模式 * @date 2021/1/304:47 下午 */ public class Hungry { private Hungry(){ } private final static Hungry HUNGRY = new Hungry(); public static Hungry getInstance(){ return HUNGRY; } }
懒汉 单例模式
/** * @author starrysky * @title: LazyMan * @projectName Juc_Pro * @description: 懒汉单例模式 * @date 2021/1/304:52 下午 */ public class LazyMan { private volatile static LazyMan lazyMan; private static boolean lockSkey = false; private LazyMan() { synchronized (LazyMan.class){ if (lockSkey == false){ lockSkey = true; }else { throw new RuntimeException("不要妄图使用反射破坏!"); } } } // 双重检测锁模式的 懒汉式单例 DCL懒汉式 public static LazyMan getInstance() { if (lazyMan == null) { synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); } } } return lazyMan; } }
静态内部类
/** * @author starrysky * @title: StaticClass * @projectName Juc_Pro * @description: 静态内部类 * @date 2021/1/307:11 下午 */ public class StaticClass { private StaticClass(){ } private static class InnerClass{ private static final Holder HOLDER = new Holder(); } private static Holder getInstenac(){ return InnerClass.HOLDER; } }
CAS
- 修内功,操作系统,计算机网络原理。
原子类
/** * @author starrysky * @title: CasUse * @projectName Juc_Pro * @description: Cas * @date 2021/1/307:54 下午 */ public class CasUse { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(12); //期望值,更新值 System.out.println(atomicInteger.compareAndSet(12, 66)); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(66, 99)); System.out.println(atomicInteger.get()); } }
Unsafe类
import java.util.function.IntUnaryOperator; import java.util.function.IntBinaryOperator; import sun.misc.Unsafe; /** * An {@code int} value that may be updated atomically. See the * {@link java.util.concurrent.atomic} package specification for * description of the properties of atomic variables. An * {@code AtomicInteger} is used in applications such as atomically * incremented counters, and cannot be used as a replacement for an * {@link java.lang.Integer}. However, this class does extend * {@code Number} to allow uniform access by tools and utilities that * deal with numerically-based classes. * * @since 1.5 * @author Doug Lea */ public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }
- ⚠️ Java无法操作内存,c++可以,Java操作C++,C++再操作内存
public final int getAndAddInt(Object o, long offset, int delta) { int v; do { //内存中的值 v = getIntVolatile(o, offset); //自旋锁,CAS : 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环! } while (!compareAndSwapInt(o, offset, v, v + delta)); return v; }
- 缺点:
1、 循环会耗时
2、一次性只能保证一个共享变量的原子性
3、ABA问题
ABA问题(立马换太子)
/** * @author starrysky * @title: CasABAProblem * @projectName Juc_Pro * @description: ABA问题, 立马换太子问题 * @date 2021/1/308:03 下午 */ public class CasABAProblem { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(20); //====================捣乱换太子============= atomicInteger.compareAndSet(20,66); atomicInteger.compareAndSet(66,20); //==================== 不知情的线程=========== atomicInteger.compareAndSet(20,99); System.out.println(atomicInteger.get()); } }
原子引用
- 解决ABA 问题,引入原子引用! 对应的思想:乐观锁!
- 带版本号 的原子操作!
试验代码
package icu.lookyousmileface.cas; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicStampedReference; /** * @author starrysky * @title: ReferenceUse * @projectName Juc_Pro * @description: AtomicStampedReference * @date 2021/1/308:17 下午 */ public class ReferenceUse { public static void main(String[] args) { AtomicStampedReference<Integer> atomicRefe = new AtomicStampedReference<>(1, 1); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 5, Runtime.getRuntime().availableProcessors(), 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() ); try { threadPoolExecutor.execute(()->{ int stamp = atomicRefe.getStamp(); System.out.println(Thread.currentThread().getName()+"=>"+stamp); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } atomicRefe.compareAndSet(1,2,atomicRefe.getStamp(),atomicRefe.getStamp()+1); System.out.println(Thread.currentThread().getName()+"=>"+atomicRefe.getStamp()); System.out.println(atomicRefe.compareAndSet(2, 1, atomicRefe.getStamp(), atomicRefe.getStamp() + 1)); System.out.println(Thread.currentThread().getName()+"=>"+atomicRefe.getStamp()); }); /** * 和乐观锁的原理一样 */ threadPoolExecutor.execute(()->{ int stamp = atomicRefe.getStamp(); System.out.println(Thread.currentThread().getName()+"=>"+stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicRefe.compareAndSet(1, 2, stamp, stamp + 1)); System.out.println(Thread.currentThread().getName()+"=>"+atomicRefe.getStamp()); }); } catch (Exception e) { e.printStackTrace(); } finally { threadPoolExecutor.shutdown(); } } }
⚠️ Tips:Integer 使用了对象缓存机制,默认范围是 -128 ~ 127 ,推荐使用静态工厂方法 valueOf 获取对象实例,而不是 new,因为 valueOf 使用缓存,而 new 一定会创建新的对象分配新的内存空间;