- LongAdder()
- ReentrantLock
- CountDownLatch(倒数的门闩)
先看如下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可以完成与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去打断线程的执行。
数到了一定数量然后门闩就开了
如下代码首先通过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更灵活一点。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)