多线程与高并发学习三

多线程与高并发学习三,第1张

线程与高并发学习三

多线程与高并发学习三
    • LongAdder()
    • ReentrantLock
    • CountDownLatch(倒数的门闩)

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更灵活一点。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5697666.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存