java.util.concurrent
以下是atomic包下的
AtomicInteger
Unsafe类:提供的方法可以直接访问内存、线程。
属性:Unsafe、int value
通过Unsafe方法中的CAS循环,保证int类型值的原子操作
java
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
AtomicInteger
属性:Unsafe、int value 0、1代表true、false
保证boolean类型值的原子操作
AtomicIntegerArray
属性:Unsafe、int[] array
保证int[]数组中的每一个索引元素的原子操作
AtomicMarkableReference
保证对一个对象和一个boolean类型的标识符的原子操作。
java
A initialRef = new A();
boolean initialMark = false;
AtomicMarkableReference<A> amr = new AtomicMarkableReference<>(initialRef, initialMark);
boolean[] markHolder = new boolean[1];
A expectedReference = amr.get(markHolder); // amr中的对象引用
boolean expectedMark = markHolder[0]; // amr中的标识符
A newReference = new A();
boolean newMark = true;
System.out.println(amr.compareAndSet(expectedReference, newReference, expectedMark, newMark));
AtomicReference
实现对一个对象引用的原子操作。
java
AtomicReference<A> ar = new AtomicReference<>();
A origin = ar.get();
// origin = new A();
A newA = new A();
System.out.println(ar.compareAndSet(origin, newA));
AtomicStampedReference
实现对对象引用和int类型的版本戳的原子操作,
解决ABA问题(A修改为B,再修改回A,另一个线程的CAS误认为A没有修改,导致CAS成功)
java
A initialRef = new A();
int initialStamp = 0;
AtomicStampedReference<A> asr = new AtomicStampedReference<>(initialRef, initialStamp);
int[] stampHolder = new int[1];
A expectedReference = asr.get(stampHolder);
int expectedStamp = stampHolder[0];
A newReference = new A();
int newStamp = expectedStamp + 1;
System.out.println(asr.compareAndSet(expectedReference, newReference, expectedStamp, newStamp));
LongAdder
以空间换时间,内部有多个单元格数组,add时,可能在数组的不同位置进行CAS,避免了CAS的冲突,提高的CAS的成功率。
相较于AtomicInteger,性能高、但内存开销大。
java
ExecutorService threadPool = Executors.newFixedThreadPool(5);
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
threadPool.execute(() -> {
for (int j = 0; j < 100000; j++) {
la.increment();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
System.out.println(la.sum());
System.out.println(la.intValue());
一下是locks包下的
ReentrantLock
TODO:JRB 原理
java
static int num = 0;
static ReentrantLock rl = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(5);
CountDownLatch countDownLatch = new CountDownLatch(2);
threadPool.execute(() -> {
rl.lock();
try {
TimeUnit.SECONDS.sleep(3); // 保证第二个线程的tryLock失败
num++;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rl.unlock();
}
countDownLatch.countDown();
;
});
threadPool.execute(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
if (rl.tryLock(1, TimeUnit.SECONDS)) {
try {
num++;
} finally {
rl.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
countDownLatch.await();
System.out.println(num);
}
Condition
实现等待通知机制。
java
public class MyTest {
static ReentrantLock reentrantLock = new ReentrantLock();
static Condition condition = reentrantLock.newCondition();
static int num = 1;
public static void main(String[] args) throws InterruptedException {
// 两个线程交替打印
two();
}
private static void two() throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 1; i < 100; i++) {
reentrantLock.lock();
try {
while (num % 2 == 0) {
condition.await(); // 释放锁并等待
}
System.out.println("线程1===>" + i);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
});
Thread t2 = new Thread(() -> {
for (int i = 1; i < 100; i++) {
reentrantLock.lock();
try {
while (num % 1 == 0) {
condition.await(); // 释放锁并等待
}
System.out.println("线程2===>" + i);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("END");
}
}
ReentrantReadWriteLock
用于优化读多写少场景下的性能。
读写锁不能被同时拥有。
在没有线程拥有写锁的前提下,多个线程可以同时拥有读锁。
在没有线程拥有读锁的前提下,只能有一个线程拥有写锁。
存在问题:拥有读锁不断被使用,导致写锁可能长时间无法获取。
java
public class MyTest {
static int num = 0;
static ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock();
static ReentrantReadWriteLock.ReadLock readLock = rrwl.readLock();
static ReentrantReadWriteLock.WriteLock writeLock = rrwl.writeLock();
public static void main(String[] args) throws InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(5);
CountDownLatch countDownLatch = new CountDownLatch(3);
threadPool.execute(() -> {
readLock.lock();
try {
TimeUnit.SECONDS.sleep(4);
System.out.println("线程1==>" + num);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readLock.unlock();
}
countDownLatch.countDown();
});
threadPool.execute(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
if (readLock.tryLock(1, TimeUnit.SECONDS)) {
try {
System.out.println("线程2==>" + num);
} finally {
readLock.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
threadPool.execute(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
if (writeLock.tryLock(1, TimeUnit.SECONDS)) {
try {
num++;
} finally {
writeLock.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
countDownLatch.await();
System.out.println(num);
}
}
StampedLock
读写锁,与ReentrantReadWriteLock的区别是提供一种新的读锁:乐观读锁。
获取乐观读锁的操作并不会阻塞其他线程获取读锁,但是我们要校验读锁的有效性,即中间是否有其他线程获取了写锁,如果有,再尝试获取读锁。
java
public class MyTest {
static int num = 0;
static StampedLock sl = new StampedLock();
public static void main(String[] args) throws InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(5);
CountDownLatch countDownLatch = new CountDownLatch(3);
threadPool.execute(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
long stamp = sl.tryOptimisticRead();
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
System.out.println(stamp + "悲观线程1===>" + num);
} finally {
sl.unlockRead(stamp);
}
} else {
System.out.println(stamp + "线程1===>" + num);
}
countDownLatch.countDown();
/*long stamp = sl.readLock();
try {
TimeUnit.SECONDS.sleep(4);
System.out.println(stamp + "线程1===>" + num);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
sl.unlockRead(stamp);
}
countDownLatch.countDown();*/
});
threadPool.execute(() -> {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
long stamp = sl.tryOptimisticRead();
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
System.out.println(stamp + "悲观线程2===>" + num);
} finally {
sl.unlockRead(stamp);
}
} else {
System.out.println(stamp + "线程2===>" + num);
}
countDownLatch.countDown();
});
threadPool.execute(() -> {
/*try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
long stamp = sl.writeLock();
try {
TimeUnit.SECONDS.sleep(2);
num++;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
sl.unlockWrite(stamp);
}
countDownLatch.countDown();
});
countDownLatch.await();
System.out.println(num);
}
}