LongAdder()
先看如下demo:
这里采用三种方式去实现count的递增,分别是Atomic,sychronized和LongAdder(),此处的结果发现LongAdder的执行速度是最快的,但是当循环次数变少,或者是线程数量变少LongAdder则不一定是最快的了,实际上采用哪种方式应该考虑以后程序并发率到底有多高,如果没有特别高的情况下,会发现未必Atomic或者LongAdder就是最好的。
public class AtomicVsSyncVsLongAdder {
private static final int THREAD_COUNT = 50;
private static final int LOOPS_COUNT = 100_0000;
static long count2 = 0L;
static AtomicLong count1 = new AtomicLong(0L);
static LongAdder count3 = new LongAdder();
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < threads.length; i++) {
threads[i] =
new Thread(() -> {
for (int k = 0; k < LOOPS_COUNT; k++) count1.incrementAndGet();
});
}
long start = System.currentTimeMillis();
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
long end = System.currentTimeMillis();
//TimeUnit.SECONDS.sleep(10);
System.out.println("Atomic: " + count1.get() + " time " + (end - start));
//-----------------------------------------------------------
Object lock = new Object();
for (int i = 0; i < threads.length; i++) {
threads[i] =
new Thread(new Runnable() {
@Override
public void run() {
for (int k = 0; k < LOOPS_COUNT; k++)
synchronized (lock) {
count2++;
}
}
});
}
start = System.currentTimeMillis();
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
end = System.currentTimeMillis();
System.out.println("Sync: " + count2 + " time " + (end - start));
//----------------------------------
for (int i = 0; i < threads.length; i++) {
threads[i] =
new Thread(() -> {
for (int k = 0; k < LOOPS_COUNT; k++) count3.increment();
});
}
start = System.currentTimeMillis();
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
end = System.currentTimeMillis();
//TimeUnit.SECONDS.sleep(10);
System.out.println("LongAdder: " + count1.longValue() + " time " + (end - start));
}
}
运行结果如下:

sychronize慢的原因是因为它是加锁的,而且可能是重量级锁,LongAdder为什么比Atomic效率更高呢,其实LongAdder内部是采用的是一个分段锁,就是将值分成很多段,然后每一段给一定数量的线程去管理,最终再把各个段的值相加得到一个最终的sum。

ReentrantLock
使用ReentrantLock可以完成与synchronized同样的功能,需要注意的是ReentrantLock需要手动释放锁,但是使用synchronized锁定的话如果遇到异常,JVM会自动释放锁,但是lock必须手动释放锁。
ReentrantLock相对应sychronized的一些优势:
使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行,而sychronized是直接让线程进入阻塞状态。
看如下Demo:
public class ReentrantLock3 {
Lock lock = new ReentrantLock();
void m1() {
try {
lock.lock();
for (int i = 0; i < 3; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
void m2() {
boolean locked = false;
try {
locked = lock.tryLock(5, TimeUnit.SECONDS);
System.out.println("m2 ..." + locked);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (locked) lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLock3 rl = new ReentrantLock3();
new Thread(rl::m1).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(rl::m2).start();
}
}

如果将m1中的i换成10的话则输出如下结果:

在等待5s后获取不到锁,然后输出false,这是sychronized所没实现的一个点。
ReentrantLock可以是一个公平锁(谁等在前面谁就先执行,就是说如果有一个新来的线程,它会先看看等待队列里面有没有线程,如果有的话就进入等待队列里面等着,等别人先运行完毕)
运行demo如下:
public class ReentrantLock5 extends Thread {
private static ReentrantLock lock = new ReentrantLock(true); //参数为true代表是公平锁
public void run() {
for (int i = 0; i < 10; i++) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获得锁");
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
ReentrantLock5 rl = new ReentrantLock5();
Thread th1 = new Thread(rl);
Thread th2 = new Thread(rl);
th1.start();
th2.start();
}
}
输出如下:

下面将 private static ReentrantLock lock = new ReentrantLock(true);改为false。
输出如下:
就会看到前面一大片全是Thread-1:

ReentrantLock还有lockinterupptibly去打断线程的执行。
CountDownLatch(倒数的门闩)
数到了一定数量然后门闩就开了
如下代码首先通过latch计数100,然后通过countdown将计数的数量逐步减为零,然后通过latch.await方法阻塞计算结果。
public class TestCountDownLatch {
public static void main(String[] args) {
usingJoin();
usingCountDownLatch();
}
private static void usingCountDownLatch() {
Thread[] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
int result = 0;
for (int j = 0; j < 10000; j++) result += j;
latch.countDown();
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
try {
latch.await();//拴住门
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end latch");
}
private static void usingJoin() {
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
int result = 0;
for (int j = 0; j < 10000; j++) result += j;
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("end join");
}
}
CountDownLatch 和join的区别:
CountDownLatch相当于是一种类似于滚动发车的模式,只要这个线程执行到countdown就直接将数字递减了,而join则是要一直等着一个线程结束才能去执行下一个,CountDownLatch更灵活一点。

被折叠的 条评论
为什么被折叠?



