大纲:
一、原子操作类介绍
二、原子性类型
原子操作类介绍
多线程资源的共享,需要为其增加同步锁,保证数据的结果正确性,但是过多的同步操作可能会造成死锁,导致程序进入停滞状态,且这样的问题很难排查。而且这样对性能也有影响。所以在这种情况下就引入了原子性的控制,来解决这样的问题。
范例:没有提供同步操作观察问题
此时加入有10个线程同时在运行,每一次运行让num的变量的值加一,那么最终的结果一定是10。但是此时返回的结果是9,原因是线程是随机执行的,哪一个线程先占用CPU的资源,哪一个线程就先执行,那么这里的情况就是多个线程同时去抢占一个资源时,可能一个线程还没有递增算法操作,而下一个线程就已经为其增加好了,这样就会导致结果不正确。这个问题在最原始的解决方案就是加入同步锁(synchronized)来解决,这个方案就是当有一个线程正在执行时,就不会让其他线程执行,必须等到正在执行的线程运行完毕之后,才执行下一个线程。
package cn.txp.juc.atom; import java.util.concurrent.TimeUnit; class Count{ private int num = 0; public void addOne() { try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) {} this.num ++; } public int getNum() { return this.num; } } public class JUCAtomDemo1 { public static void main(String[] args) throws Exception{ Count count = new Count(); for(int x = 0 ; x < 10 ; x ++) { new Thread(() -> { count.addOne(); }).start(); } TimeUnit.SECONDS.sleep(2); System.out.println("最终计算结果:" + count.getNum()); // 结果 = 9 多运行几次结果不一 } }
范例:加入原始的同步锁解决
package cn.txp.juc.atom; import java.util.concurrent.TimeUnit; class Count{ private int num = 0; public synchronized void addOne() { try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) {} this.num = this.num + 1; System.out.println(this.num); } public synchronized int getNum() { return this.num; } } public class JUCAtomDemo1 { public static void main(String[] args) throws Exception{ Count count = new Count(); for(int x = 0 ; x < 10 ; x ++) { System.out.println(x); new Thread(() -> { count.addOne(); }).start(); } TimeUnit.SECONDS.sleep(2); System.out.println("最终计算结果:" + count.getNum()); } }
范例:用原子类实现,此时避免了大量的使用synchronized(同步锁)和唤醒处理机制。
package cn.txp.juc.atom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; class Count{ private AtomicInteger num = new AtomicInteger(); public void addOne() { try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) {} this.num.addAndGet(1); // 加一 } public AtomicInteger getNum() { return this.num; } } public class JUCAtomDemo1 { public static void main(String[] args) throws Exception{ Count count = new Count(); for(int x = 0 ; x < 10 ; x ++) { System.out.println(x); new Thread(() -> { count.addOne(); }).start(); } TimeUnit.SECONDS.sleep(2); System.out.println("最终计算结果:" + count.getNum()); } }
原子性类型
在JUC中,原子性类型分为了一下几种:
1、基本原子类型
2、原子的数组类型
3、原子的引用类型
4、原子的属性修改器
5、原子计算器
1、基本原子类型
主要描述的是一些常规的数据操作,有AtomicBoolean, AtomicInteger, AtomicLong这三种对应类型,这几种对应的类型操作都非常的类似,上面的一个demo中就是使用AtomicInteger来进行操作的。
常用操作方法:
public AtomicLong() {} | 设置保存的类型为0 |
public AtomicLong(long num) {} | 设置指定的初始化类容 |
public final long addAndGet(long delta) {} | 增加数据同时返回计算后的结果 |
public final long decrementAndGet() {} | 自减并返回结果 |
public final long incrementAndGet() {} | 自增并返回结果 |
public final void set(long newValue) {} | 设置新的数据内容 |
public final long get() {} | 获取原始的数据内容 |
public final boolean compareAndSet(long expectedValue, long newValue) {} | CAS操作,进行数据的安全操作修改 |
范例:
package cn.txp.juc.atom; import java.util.concurrent.atomic.AtomicLong; public class JUCAtomDemo2 { public static void main(String[] args) throws Exception{ AtomicLong al = new AtomicLong(10); System.out.println("增加并返回结果:" + al.addAndGet(10)); al.set(99); System.out.println(al.get()); } }
范例:compareAndSet()方法是采用CAS定义的,CAS是乐观锁的处理机制,CAS进行操作的时候并不是利用Java虚拟机来实现的,而是直接利用底层的交互类来实现的,也就是说CAS是利用最底层的代码实现的。CAS的操作就是将当前的内容与预期的内容进行判断,如果相同则进行内容的交换。
package cn.txp.juc.atom; import java.util.concurrent.atomic.AtomicLong; public class JUCAtomDemo3 { public static void main(String[] args) throws Exception{ AtomicLong al = new AtomicLong(10); System.out.println(al.compareAndSet(20, 70)); System.out.println(al.get()); } }
2、原子数组类型
原子数组类型有如下几个类:AtomicIntegerArray<整型数组>、AtomicLongArray<长整型数组>、AtomicReferenceArray<引用数组>。
AtomicReferenceArray为例:
方法 | 描述 |
public AtomicReferenceArray(int length) | 定义要操作数组的空白长度 |
public AtomicReferenceArray(E[] array) | 设置直接要操作的数组 |
public final boolean compareAndSet(int i , E expectedValue, E newValue) | 进行数组的比较替换操作 |
public final E get(int i) | 获取指定索引的数组 |
public final void set(int i , E newValue) | 设置指定索引的数组 |
范例:
package cn.txp.juc.atom.array; import java.util.concurrent.atomic.AtomicReferenceArray; public class JucAtomicReferenceArrayDemo { public static void main(String[] args) { AtomicReferenceArray<String> array = new AtomicReferenceArray<String>(5); array.set(0, "www.baidu.com"); array.set(1, "test"); array.set(2, "array"); System.out.println(array.compareAndSet(1, "test", "dd")); System.out.println(array.get(1)); } }
3、原子的引用类型
引用原子操作类有几个常用类:AtomicReference 引用类型原子类、AtomicMarkableReference 标记节点原子引用类、AtomicStampedReference 引用版本号原子类
方法 | 描述 |
public AtomicReference() | 设置一个空引用的数据内容 |
public AtomicReference(V initValue) | 设置要引用的数据内容 |
public final boolean compareAndSet(V expectedValue, V newValue) | CAS比较替换操作 |
public final V get() | 获取设置的内容 |
public final V set(V newValue) | 设置要操作的数据内容 |
范例:AtomicReference
package cn.txp.juc.atom.reference; impor java.util.concurrent.atomic.AtomicReference; class Person{ private String name; private Integer age; public Person(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "name: " + this.name + "\t" + "age: " + this.age; } } public class JucRefrenceDemo { public static void main(String[] args) { Person per1 = new Person("A1", 10); Person per2 = new Person("C", 10); AtomicReference<Person> atomicRe = new AtomicReference<>(per1); System.out.println(atomicRe.compareAndSet(per1, per2)); System.out.println(atomicRe.get()); } }
范例:AtomicStampeReference,根据版本号实现数据的修改
方法 | 描述 |
public AtomicStampedReference(V initref, int initialStamp) | 置一个初始化引用和初始化数值标记 |
public V getReference() | 获取当前引用 |
public int getStamp() | 获取当前的标记 |
public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) | CAS比较替换操作 |
public void set(V newReference, int newStamp) | 设置新的内容并指定新的标记 |
范例:AtomicStampedReference
package cn.txp.juc.atom.reference; import java.util.concurrent.atomic.AtomicStampedReference; class Member{ private String name; private Integer age; public Member(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "name: " + this.name + "\t" + "age: " + this.age; } } public class JucRefrenceDemo2 { public static void main(String[] args) { Member mem1 = new Member("testMember", 10); Member mem2 = new Member("testMem", 20); AtomicStampedReference<Member> atomicMember = new AtomicStampedReference<Member>(mem1, 1); atomicMember.compareAndSet(mem2, mem2, 1, 3); System.out.println(atomicMember.getReference()); System.out.println(atomicMember.getStamp()); } }
范例:AtomicMarkableReference类,在使用AtomicStampedReference类的时候,需要每一次给一个版本作为标识,这样在开发中过于麻烦,AtomicMarkableReference就属于它的简化操作。
方法 | 描述 |
public AtomicMarkableReference(V initRefe, boolean initMarkable) | 初始化内容并设置一个标记 |
public boolean isMarked() | 获取当前的标记 |
public V getReference() | 获取当前引用数据 |
public void set(V newReference, boolean newMark) | 设置引用数据并设置标记 |
public boolean compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark) | 比较替换内容 |
范例:AtomicMarkableReference
package cn.txp.juc.atom.reference; import java.util.concurrent.atomic.AtomicMarkableReference; class News{ private String title; private String content; public News(String title, String content) { this.title = title; this.content = content; } @Override public String toString() { return "title: " + this.title + "\t" + "content: " + this.content; } } public class JucRefrenceDemo3 { public static void main(String[] args) { News news = new News("title1", "内容"); News newss = new News("titd1", "内容3"); AtomicMarkableReference<News> mark = new AtomicMarkableReference<News>(news, true); mark.compareAndSet(news, newss, true, false); System.out.println(mark.isMarked()); System.out.println(mark.getReference()); System.out.println(mark.isMarked()); } }
4、原子的属性修改器
原子的引用类型是将对象放到原子类中进行保护,从而实现同步的处理,但是这样的操作无法同步类中的属性,如果要同步属性,可以通过原子的属性修改器来完成。
原子的属性修改器有几个常用类:AtomicIntegerFieldUpdater(针对于Integer类型进行保护)、AtomicLongFieldUpdater(针对于Long类型进行保护)、AtomicReferenceFieldUpdater(针对于引用进行保护)
方法 | 描述 |
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) | 获取属性修改器实例对象 |
public abstract boolean compareAndSet(T obj, int expect, int update) | 比较替换操作。 |
范例:AtomicIntegerFieldUpdater
package cn.txp.juc.atom.field; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; class Member{ private String name; private volatile int age; public Member(String name, int age) { this.name = name; this.age = age; } public void setName(String name) { this.name = name; } public void setAge(int age) { AtomicIntegerFieldUpdater fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Member.class, "age"); fieldUpdater.compareAndSet(this, this.age, age); } @Override public String toString() { return "name:" + this.name + "age" + this.age; } } public class IntegerFieldUpdater { public static void main(String[] args) { Member mem = new Member("名称", 20); mem.setAge(15); System.out.println(mem); } }
5、原子计算器
并发计算操作中,提供累加器(LongAccumulator、DoubleAccumulator)和加法器(LongAdder、DoubleAdder)的概念。
范例:使用累加器进行计算
package cn.txp.juc.atom.adder; import java.util.concurrent.atomic.DoubleAccumulator; public class AdderDemo1 { public static void main(String[] args) { DoubleAccumulator doubleAccumulator = new DoubleAccumulator((x, y) -> x + y, 1.1); doubleAccumulator.accumulate(20); System.out.println(doubleAccumulator.get()); } }
package cn.txp.juc.atom.adder; import java.util.concurrent.atomic.DoubleAdder; public class AdderDemo2 { public static void main(String[] args) { DoubleAdder adder = new DoubleAdder(); adder.add(10); adder.add(20); System.out.println(adder); } }
知乎文章: https://www.zhihu.com/people/tan-xu-peng-44
编程专业知识:https://www.cnblogs.com/tjava
编程开发技术问题解决CSDN:https://blog.csdn.net/qq_37291829