首页 > 编程语言 >Java并发编程一引用类型、升级类型原子类初使用加源码分析

Java并发编程一引用类型、升级类型原子类初使用加源码分析

时间:2022-11-09 18:38:33浏览次数:48  
标签:Java Thread int 类初 next 源码 obj prev public


推荐:​​Java并发编程汇总​​

Java并发编程一引用类型、升级类型原子类初使用加源码分析

首先我们来看一看有哪些原子类。

Java并发编程一引用类型、升级类型原子类初使用加源码分析_原子类

Java并发编程一引用类型、升级类型原子类初使用加源码分析_原子类_02


现在我们来看看该如何去使用这些引用类型、升级类型原子类吧。

之前已经介绍过基本类型、数组类型原子类和累加器的使用了,讲过的原理这里就不会再涉及了,想了解就看下面这篇博客吧。

​​Java并发编程一基本类型、数组类型原子类和累加器初使用加源码分析​​

AtomicReference

​AtomicReference​​类会将一个引用类型包装成原子类,现在我们来看一看如何使用吧。

这里我们使用​​AtomicReference​​​类来实现一个简易版自旋锁。
代码:

package lock.spinlock;

import java.util.concurrent.atomic.AtomicReference;

public class SpinLock {

private AtomicReference<Thread> sign = new AtomicReference<>();

public void lock() {
Thread current = Thread.currentThread();
while (!sign.compareAndSet(null, current)) {
System.out.println("自旋获取失败,再次尝试");
}
}

public void unlock() {
Thread current = Thread.currentThread();
sign.compareAndSet(current, null);
}

public static void main(String[] args) {
SpinLock spinLock = new SpinLock();
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始尝试获取自旋锁");
spinLock.lock();
System.out.println(Thread.currentThread().getName() + "获取到了自旋锁");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "释放了自旋锁");
spinLock.unlock();
}
}
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
thread1.start();
thread2.start();
}
}

开头输出:

Thread-1开始尝试获取自旋锁
Thread-0开始尝试获取自旋锁
Thread-1获取到了自旋锁
自旋获取失败,再次尝试
自旋获取失败,再次尝试
自旋获取失败,再次尝试
自旋获取失败,再次尝试
自旋获取失败,再次尝试

结尾输出:

自旋获取失败,再次尝试
自旋获取失败,再次尝试
自旋获取失败,再次尝试
Thread-0释放了自旋锁
自旋获取失败,再次尝试
Thread-1获取到了自旋锁
Thread-1释放了自旋锁

中间输出还有很多的​​自旋获取失败,再次尝试​​。

进入​​AtomicReference​​​类源码,可以看到​​AtomicReference​​​类将引用类型包装成原子类型的关键-​​Unsafe​​​工具类,​​Unsafe​​工具类在之前的那篇博客已经介绍过了,这里就不再赘述。

private static final Unsafe unsafe = Unsafe.getUnsafe();

同样使用反射来获取要操作的属性。

static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}

使用​​volatile​​​ 关键字修饰​​value​​​属性,保证了​​value​​属性的可见性,这和之前讲过的原子类是一样的。

private volatile V value;

我们来看看​​AtomicReference​​类有哪些常用的方法。

/**
* Gets the current value.
*
* @return the current value
*/
public final V get() {
return value;
}

​get()​​​会获取当前的​​value​​。

/**
* Sets to the given value.
*
* @param newValue the new value
*/
public final void set(V newValue) {
value = newValue;
}

​set(V newValue)​​​会进行赋值操作,赋值操作是原子操作,又因为用​​volatile​​​ 关键字修饰了​​value​​​属性,保证了​​value​​属性的可见性,这次赋值操作,对其他线程是可见的。

/**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
@SuppressWarnings("unchecked")
public final V getAndSet(V newValue) {
return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
}

​getAndSet(V newValue)​​会先获取旧值再赋新值,和之前介绍过的基本类型原子类一样,这些方法都是原子操作,不然就会有线程安全的问题了。

就介绍这些常用的方法吧,其他的方法大家可以自己去看看源码。

AtomicIntegerFieldUpdater

​AtomicIntegerFieldUpdater​​​类,根据类名我们可能就已经知道它的作用了,它是将一个引用类型的​​int(自动装箱即可)或者Integer​​类型的属性升级为与原子类型。

我们来看一看它的用法吧。
代码:

package atomic;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class AtomicIntegerFieldUpdaterDemo implements Runnable{

static Candidate tom;
static Candidate peter;

public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater
.newUpdater(Candidate.class, "score");

@Override
public void run() {
for (int i = 0; i < 10000; i++) {
peter.score++;
scoreUpdater.getAndIncrement(tom);
}
}

public static class Candidate {

volatile int score;
}

public static void main(String[] args) throws InterruptedException {
tom=new Candidate();
peter=new Candidate();
AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("普通变量:"+peter.score);
System.out.println("升级后的结果"+ tom.score);
}
}

输出:

普通变量:18860
升级后的结果20000

很明显,普通变量的​​++​​​操作存在线程安全问题,而经过使用​​AtomicIntegerFieldUpdater​​类进行升级的变量就没有线程安全问题了(使用正确的情况下)。

从这段代码可以很容易看出来,这里也使用了反射。

public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater
.newUpdater(Candidate.class, "score");
/**
* Creates and returns an updater for objects with the given field.
* The Class argument is needed to check that reflective types and
* generic types match.
*
* @param tclass the class of the objects holding the field
* @param fieldName the name of the field to be updated
* @param <U> the type of instances of tclass
* @return the updater
* @throws IllegalArgumentException if the field is not a
* volatile integer type
* @throws RuntimeException with a nested reflection-based
* exception if the class does not hold field or is the wrong type,
* or the field is inaccessible to the caller according to Java language
* access control
*/
@CallerSensitive
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>
(tclass, fieldName, Reflection.getCallerClass());
}

使用​​AtomicIntegerFieldUpdater​​​类进行升级的变量其实就具有了和​​AtomicInteger​​类一样的功能。

部分源码:

public abstract int get(T obj);

public int getAndSet(T obj, int newValue) {
int prev;
do {
prev = get(obj);
} while (!compareAndSet(obj, prev, newValue));
return prev;
}

public int getAndIncrement(T obj) {
int prev, next;
do {
prev = get(obj);
next = prev + 1;
} while (!compareAndSet(obj, prev, next));
return prev;
}

public int getAndDecrement(T obj) {
int prev, next;
do {
prev = get(obj);
next = prev - 1;
} while (!compareAndSet(obj, prev, next));
return prev;
}

public int getAndAdd(T obj, int delta) {
int prev, next;
do {
prev = get(obj);
next = prev + delta;
} while (!compareAndSet(obj, prev, next));
return prev;
}

public int incrementAndGet(T obj) {
int prev, next;
do {
prev = get(obj);
next = prev + 1;
} while (!compareAndSet(obj, prev, next));
return next;
}

public int decrementAndGet(T obj) {
int prev, next;
do {
prev = get(obj);
next = prev - 1;
} while (!compareAndSet(obj, prev, next));
return next;
}

public int addAndGet(T obj, int delta) {
int prev, next;
do {
prev = get(obj);
next = prev + delta;
} while (!compareAndSet(obj, prev, next));
return next;
}

这些方法和​​AtomicInteger​​类中的方法差不多,就不多讲了,可以参考下面这篇博客。

​​Java并发编程一基本类型、数组类型原子类和累加器初使用加源码分析​​


标签:Java,Thread,int,类初,next,源码,obj,prev,public
From: https://blog.51cto.com/u_15870611/5837716

相关文章

  • Java并发编程一ReentrantReadWriteLock初使用
    推荐:​​Java并发编程汇总​​Java并发编程一ReentrantReadWriteLock初使用​​ReentrantReadWriteLock​​是一种读写锁,从类名也可以看出来。​​ReentrantReadWriteLock​......
  • Java中String被称为不可变字符串的原因
    很多东西,看似可变,实际上不过是是新桃换旧符罢了。 代码:/***String之所以被称为不可变字符串*/staticvoidtestString(){Stringstr="i......
  • mac下java和mvn的环境配置
    原文:https://blog.csdn.net/w605283073/article/details/111770386   https://www.pudn.com/news/62f8c6905425817ffc462029.htmlmvn打包报错:Nocompilerisprov......
  • JAVA遍历Map所有元素
    //JDK1.5Mapmap=newHashMap();for(Objectobj:map.keySet()){Objectkey=obj;Objectvalue=map.get(obj);}123456//JDK1.4......
  • java 串口工具jSerialComm
    由于项目之前用的串口工具RXTX只支持到jdk1.8然后项目目前用到的jdk是13的所以在网上找了一下找到了这个 jSerialComm目前使用是支持13及1.8的没做其它jdk版本测试......
  • Java线程安全
    线程安全的本质其实第一张图的例子是有问题的,主内存中的变量是共享的,所有线程都可以访问读写,而线程工作内存又是线程私有的,线程间不可互相访问。那在多线程场景下,图上的线程......
  • Caused by: java.lang.NoClassDefFoundError: net/minidev/asm/FieldFilter 报错的解
    Causedby:org.springframework.beans.factory.BeanCreationException:Errorcreatingbeanwithname'requestMappingHandlerAdapter'definedinclasspathresourc......
  • JavaScript 清空对象的值
      functionclearObj(obj){//判断是不是对象if(!Object.prototype.toString.call(obj)=="[objectObject]"){returnfalse;}for(constkey......
  • javascript基础知识之多维数组扁平化去重排序
    vararr=[1,2,3,4,5,[1,2,6,8]]=>[1,2,3,4,5,6,8]vararr=[1,2,3,4,5,[1,2,6,8]]//js方法实现varflatArr=arr.toString().split(",")//扁平functionun......
  • 2022 年你需要掌握的 7 种关于 JavaScript 的数组方法(下)
    4.Array.forEach()现在这是一个经典。那个 forEach() 方法的工作原理很像常规 for 循环。它在数组上循环并对每个项目执行一个函数。 .forEach() 的第一个参数是......