JDK源码

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);
    }
}
相关推荐
慕y27416 分钟前
Java学习第九十六部分——Eureka
java·学习·eureka
头发那是一根不剩了42 分钟前
信创应用服务器TongWeb安装教程、前后端分离应用部署全流程
java·信创·tongweb
22:30Plane-Moon1 小时前
Servlet作用域,监听器,JSP九大内置对象
java·开发语言·servlet
小白(猿)员1 小时前
JVM、JDK、JRE的区别
java·开发语言·jvm
axban2 小时前
QT中删除控件的注意事项、deleteLater和delete的区别
java·数据库·qt
Resean02233 小时前
SpringMVC 6+源码分析(二)DispatcherServlet实例化流程 1
java·spring boot·spring·servlet·springmvc
泉城老铁3 小时前
Spring Boot 对接阿里云 OSS 的详细步骤和流程
java·后端·程序员
陈平安安4 小时前
Maven学习
java·maven
-$_$-4 小时前
【笔试真题】2024秋招京东后端开发岗位-第一批笔试
java·开发语言