diff --git a/week_02/35/AtomicInteger-035.md b/week_02/35/AtomicInteger-035.md new file mode 100644 index 0000000000000000000000000000000000000000..e4304e1c640bccab79792b77bd879019e3e5e931 --- /dev/null +++ b/week_02/35/AtomicInteger-035.md @@ -0,0 +1,4 @@ + AtomicInteger + AtomicInteger提供原子操作Integer的类 + 在Java语言中,i++和++i操作并不是线程安全的,在使用的时候不可避免造成数据不同步 + AtomicInteger提供线程安全的加减操作接口 \ No newline at end of file diff --git a/week_02/35/AtomicStampedReference-035.md b/week_02/35/AtomicStampedReference-035.md new file mode 100644 index 0000000000000000000000000000000000000000..80941dc74b3d549827101a16eb7fe5fcfc39ef0c --- /dev/null +++ b/week_02/35/AtomicStampedReference-035.md @@ -0,0 +1,52 @@ +####AtomicReference + AtomicReference:顾名思义,就是以原子方式更新对象引用。 + AtomicReference持有一个对象的引用——value,并通过Unsafe类来操作该引用 + AtomicReference提供了以无锁方式访问共享资源的能力 + + 为什么需要AtomicReference?难道多个线程同时对一个引用变量赋值也会出现并发问题? + 引用变量的赋值本身没有并发问题,也就是说对于引用变量demo ,类似下面的赋值操作本身就是原子操作: + Demo demo = ... ; + * AtomicReference的引入是为了可以用一种类似乐观锁的方式操作共享资源,在某些情景下以提升性能。 + + public final boolean compareAndSet(V expect, V update) { + return unsafe.compareAndSwapObject(this, valueOffset, expect, update); + } + 该方法会将入参的expect变量所指向的对象和AtomicReference中的引用对象进行比较 + 如果两者指向同一个对象,则将AtomicReference中的引用对象重新置为update,修改成功返回true,失败则返回false + AtomicReference其实是比较对象的引用 + +####AtomicStampedReference + CAS操作可能存在ABA的问题, 引入AtomicStampedReference + AtomicStampedReference的所有方法,其实就是Unsafe类针对这个Pair对象的操作 + 和AtomicReference相比,AtomicStampedReference中的每个引用变量都带上了pair.stamp这个版本号,这样就可以解决CAS中的ABA问题 + + public AtomicStampedReference(V initialRef, int initialStamp) { + pair = Pair.of(initialRef, initialStamp); + } + * 传入一个初始的引用变量initialRef外,还有一个initialStamp变量,initialStamp其实就是版本号(或者说时间戳),用来唯一标识引用变量 + * 在构造器内部,实例化了一个Pair对象,Pair对象记录了对象引用和时间戳信息,采用int作为时间戳,实际使用的时候,要保证时间戳唯一(一般做成自增的) + * 如果时间戳如果重复,还会出现ABA的问题。 + + AtomicStampedReference的所有方法,其实就是Unsafe类针对这个Pair对象的操作 + 和AtomicReference相比,AtomicStampedReference中的每个引用变量都带上了pair.stamp这个版本号,这样就可以解决CAS中的ABA问题 + + + + public boolean compareAndSet(V expectedReference, + V newReference, + int expectedStamp, + int newStamp) { + Pair current = pair; + return + expectedReference == current.reference && + expectedStamp == current.stamp && + ((newReference == current.reference && + newStamp == current.stamp) || + casPair(current, Pair.of(newReference, newStamp))); + } + 如果AtomicStampedReference内部pair的引用变量、时间戳与入参expectedReference、expectedStamp都一样 + 说明期间没有其它线程修改过AtomicStampedReference,可以进行修改。此时,会创建一个新的Pair对象(casPair方法,因为Pair是Immutable类) + +####AtomicMarkableReference + 只关心是否更改过,所以就有了AtomicMarkableReference + AtomicMarkableReference的唯一区别就是不再用int标识引用,而是使用boolean变量——表示引用变量是否被更改过 \ No newline at end of file diff --git a/week_02/35/Unsafe-035.md b/week_02/35/Unsafe-035.md new file mode 100644 index 0000000000000000000000000000000000000000..74b7eb3b6b8ab658c874bba8444e80354f894fe9 --- /dev/null +++ b/week_02/35/Unsafe-035.md @@ -0,0 +1,48 @@ + Unsafe类是final,不允许继承。且构造函数是private + Unsafe无法实例化,只能通过反射来获取Unsafe + Unsafe提供了一些底层操作,如直接内存访问、线程调度等 + + //theUnsafe是Unsafe类的static final方法,在Unsafe类的static方法块就已经初始化赋值 + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + // 得到Unsafe类的实例,因为是静态变量,所以不需要提供对象,直接传参null获取 + unsafe = (Unsafe) f.get(null); + + 方法: + 1、objectFieldOffset()(获取对象属性内存中的偏移量) + 要对对象的属性值直接在内存中进行cas替换,必须要知道这个属性在这个类存储的内存中的位置(偏移量) + 2、compareAndSwapObject()(cas替换到对象属性的值) + + + 主要功能: + 1.实例化私有类 + 2.CAS操作,通过内存偏移地址修改变量值 + java并发包中的SynchronousQueue中的TransferStack中使用CAS更新栈顶 + / Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long headOffset; + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class k = TransferStack.class; + headOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("head")); + } catch (Exception e) { + throw new Error(e); + } + } + //栈顶 + volatile SNode head; + boolean casHead(SNode h, SNode nh) { + return h == head && UNSAFE.compareAndSwapObject(this, headOffset, h, nh); + } + 3.直接内存访问 + Unsafe的直接内存访问:用Unsafe开辟的内存空间不占用Heap空间,当然也不具有自动内存回收功能。做到像C一样自由利用系统内存资源 + + 4.Unsafe的大部分API都是native的方法,主要包括以下几类: + * Class相关。主要提供Class和它的静态字段的操作方法 + * Object相关。主要提供Object和它的字段的操作方法 + * Arrray相关。主要提供数组及其中元素的操作方法 + * 并发相关。主要提供低级别同步原语,如CAS、线程调度、volatile、内存屏障等 + * Memory相关。提供了直接内存访问方法(绕过Java堆直接操作本地内存),可做到像C一样自由利用系统内存资源 + * 系统相关。主要返回某些低级别的内存信息,如地址大小、内存页大小 \ No newline at end of file diff --git a/week_02/35/com.dans.demo/ABATest.java b/week_02/35/com.dans.demo/ABATest.java new file mode 100644 index 0000000000000000000000000000000000000000..1d56386d0f989864ff9c209481b208a9abdd6508 --- /dev/null +++ b/week_02/35/com.dans.demo/ABATest.java @@ -0,0 +1,66 @@ +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicStampedReference; + +/** + * @author dans + * @ClassName: ABATest + * @Function: TODO + * @Date: 2019/12/19 9:53 + */ +public class ABATest { + AtomicReference atomicReference = new AtomicReference<>(10); + static AtomicStampedReference money = new AtomicStampedReference(19, 0); + + public static void main(String[] args) { + //模拟多个线程同时更新后台数据库,为用户充值 + for (int i = 0; i < 3; i++) { + //获得当前时间戳 + final int timestamp = money.getStamp(); + new Thread() { + public void run() { + while (true) { + //获得当前对象引用 + Integer m = money.getReference(); + if (m < 20) { + //比较设置 参数依次为:期望值 写入新值 期望时间戳 新时间戳 + if (money.compareAndSet(m, m + 20, timestamp, timestamp + 1)) { + System.out + .println("余额小于20元,充值成功,余额:" + money.getReference() + "元"); + break; + } + } else { + //System.out.println("余额大于20元,无需充值"); + break; + } + } + } + }.start(); + } + + //用户消费线程,模拟消费行为 + new Thread() { + public void run() { + for (int i = 0; i < 100; i++) { + while (true) { + int timestamp = money.getStamp(); + Integer m = money.getReference(); + if (m > 10) { + System.out.println("大于10元"); + if (money.compareAndSet(m, m - 10, timestamp, timestamp + 1)) { + System.out.println("成功消费10元,余额:" + money.getReference()); + break; + } + } else { + System.out.println("没有足够的金额"); + break; + } + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + } + }.start(); + } +} diff --git a/week_02/35/com.dans.demo/AtomicIntegerDemo.java b/week_02/35/com.dans.demo/AtomicIntegerDemo.java new file mode 100644 index 0000000000000000000000000000000000000000..1ddcbbe39157edbc6e86d2d7c882fc77c63e073d --- /dev/null +++ b/week_02/35/com.dans.demo/AtomicIntegerDemo.java @@ -0,0 +1,37 @@ +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author dans + * @ClassName: AtomicIntegerDemo + * @Function: TODO + * @Date: 2019/12/16 14:36 + */ +public class AtomicIntegerDemo { + + private static final int THREADS_CONUT = 20; + public static volatile AtomicInteger count = new AtomicInteger(0); + + public static void increase() { + count.incrementAndGet(); + } + + public static void main(String[] args) { + Thread[] threads = new Thread[THREADS_CONUT]; + for (int i = 0; i < THREADS_CONUT; i++) { + threads[i] = new Thread(new Runnable() { + @Override + public void run() { + for (int i = 0; i < 1000; i++) { + increase(); + } + } + }); + threads[i].start(); + } + + while (Thread.activeCount() > 1) { + Thread.yield(); + } + System.out.println(count); + } +} diff --git a/week_02/35/com.dans.demo/AtomicStampedReferenceDemo.java b/week_02/35/com.dans.demo/AtomicStampedReferenceDemo.java new file mode 100644 index 0000000000000000000000000000000000000000..f8e2d5e188299ba1be5d0fd0c5e69fcaf6227371 --- /dev/null +++ b/week_02/35/com.dans.demo/AtomicStampedReferenceDemo.java @@ -0,0 +1,41 @@ +import entity.Demo; +import java.util.concurrent.atomic.AtomicStampedReference; + +/** + * @author dans + * @ClassName: AtomicStampedReferenceDemo + * @Function: TODO + * @Date: 2019/12/19 15:57 + */ +public class AtomicStampedReferenceDemo { + + private static AtomicStampedReference demoAtomicStampedReference = new AtomicStampedReference<>( + null, 0); + private static Integer i = 1; + + public static void main(String[] args) { + Demo demo = new Demo(); + demo.setId(1L); + demo.setName("test"); + i += 1; + demoAtomicStampedReference.set(demo, i); + + Integer count = demoAtomicStampedReference.getStamp(); + System.out.println(count); + demo = demoAtomicStampedReference.getReference(); + System.out.println("------" + demo.getName()); + + demo.setName("demo"); + i += 1; + demoAtomicStampedReference.set(demo, i); + + count = demoAtomicStampedReference.getStamp(); + System.out.println(count); + demo = demoAtomicStampedReference.getReference(); + System.out.println(demo.getName()); + + + demo = demoAtomicStampedReference.get(new int[]{2}); + System.out.println("------" + demo.getName()); + } +} diff --git a/week_02/35/com.dans.demo/UnsafeDemo.java b/week_02/35/com.dans.demo/UnsafeDemo.java new file mode 100644 index 0000000000000000000000000000000000000000..61b7d5be9c72e9aef4f9aa4d30cb8957ad826439 --- /dev/null +++ b/week_02/35/com.dans.demo/UnsafeDemo.java @@ -0,0 +1,50 @@ +import entity.Demo; +import java.lang.reflect.Field; +import sun.misc.Unsafe; + +/** + * @author dans + * @ClassName: UnsafeDemo + * @Function: TODO + * @Date: 2019/12/16 9:52 + */ +public class UnsafeDemo { + + public static void main(String[] args) { + // 获取 Unsafe 内部的私有的实例化单例对象 + Field field = null; + Unsafe unsafe = null; + try { + field = Unsafe.class.getDeclaredField("theUnsafe"); + // 无视权限 + field.setAccessible(true); + unsafe = (Unsafe) field.get(null); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + + Demo demo = new Demo(); + demo.setId(1L); + demo.setName("123"); + + Class demoClass = demo.getClass(); + try { + Field fieldId = demoClass.getDeclaredField("id"); + Long offset = unsafe.objectFieldOffset(fieldId); + unsafe.compareAndSwapObject(demo, offset, 1L, 2L); + System.out.println(demo.getId()); + + Demo usDemo = (Demo) unsafe.allocateInstance(Demo.class); + usDemo.setName("Hello World"); + System.out.println(usDemo.getName()); + + // 指针大小 + System.out.println(unsafe.addressSize()); + // 内存页大小 + System.out.println(unsafe.pageSize()); + + } catch (NoSuchFieldException | InstantiationException e) { + e.printStackTrace(); + } + } +} diff --git a/week_02/35/com.dans.demo/entity/Demo.java b/week_02/35/com.dans.demo/entity/Demo.java new file mode 100644 index 0000000000000000000000000000000000000000..80d97cc48fd97cc819f0ca4158bb2c44892d8a15 --- /dev/null +++ b/week_02/35/com.dans.demo/entity/Demo.java @@ -0,0 +1,40 @@ +package entity; + +import java.util.Date; + +/** + * @author dans + * @ClassName: Demo + * @Function: TODO + * @Date: 2019/12/16 9:59 + */ +public class Demo implements java.io.Serializable { + public static final String SNAME = "demo"; + private Long id; + private String name; + private Date date; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } +}