Java 并发编程实战

本文深入探讨Java并发编程,包括CopyOnWrite容器实现读写分离、乐观锁的应用、BlockingQueue在生产者消费者问题中的使用、CountDownLatch的多线程闭锁、CyclicBarrier的线程栅栏以及Semaphore信号量机制。通过实例解析各种并发工具的实战技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主要内容:
1. 读写分离实战之 CopyOnWrite
2. 写写分离实战之乐观锁
3. 生产者消费者实战之 BlockingQueue
4. 多线程闭锁实战之 CountDownLatch
5. 多线程栅栏实战之 CyclicBarrier
6. 并发编程实战之信号量机制 Semaphore


1. 读写分离实战之CopyOnWrite

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。

public class CopyOnWriteTest {

    public static void main(String args[]) {
        /** 换成ArrayList后,读写同时进行会抛异常 */
        final List<String> list = new CopyOnWriteArrayList<String>();
            int cnt = 10000;
            @Override
            public void run() {
                while (true) {
                    try {
                       Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    list.add(cnt++ + "");
                }
            }
        });
        write.setDaemon(true);
        write.start();

        Thread read1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                      e.printStackTrace();
                    }
                    System.out.println("1>>:" + list.size() + list.toString());
                }

            }
        });

        Thread read2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("2>>:" + list.size() + list.toString());
                }

            }
        });
        read2.start();

    }
}
2. 写写分享实战之乐观锁
//todo
3. 生产者消费者实战之 BlockingQueue

阻塞队列 BlockingQueue,一个线程将会持续生产新对象并将其插入到队列之中,直到队列达到它所能容纳的临界点。也就是说,它是有限的。如果该阻塞队列到达了其临界点,负责生产的线程将会在往里边插入新对象时发生阻塞。它会一直处于阻塞之中,直到负责消费的线程从队列中拿走一个对象。
负责消费的线程将会一直从该阻塞队列中拿出对象。如果消费线程尝试去从一个空的队列中提取对象的话,这个消费线程将会处于阻塞之中,直到一个生产线程把一个对象丢进队列。

public class BlockingQueueTest{

    private static LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<String>();


        /** 生产者1 */
        Thread write1 = new Thread(new Runnable(){
            @Override
            public void run() {
                int cnt = 10000;
                while(true){
                    try {
                        Thread.sleep(2000);
                        queue.put("w1-" + cnt);
                        cnt += 2;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        write1.start();

        /** 生产者2 */
        Thread write2 = new Thread(new Runnable(){
            @Override
            public void run() {
                int cnt = 10001;
                while(true){
                    try {
                        Thread.sleep(2000);
                        queue.put("w2-" + cnt);
                        cnt += 2;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        /** 消费者1 */
        Thread read1 = new Thread(new Runnable(){
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.sleep(1000);
                        System.out.println("r1-" + queue.take());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        read1.start();

        /** 消费者2 */
        Thread read2 = new Thread(new Runnable(){
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.sleep(1000);
                        System.out.println("r2-" + queue.take());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

    }
}
4. 多线程闭锁实战之 CountDownLatch

CountDownLatch一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 用给定的计数初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class CountDownLatchTest {

    private static Executor exec = Executors.newFixedThreadPool(5);

    public static void main(String args[]) throws InterruptedException {

        long timerAll = System.currentTimeMillis();

        // 计数到8
        final CountDownLatch countDownLatch = new CountDownLatch(8);

        for (int i = 0; i < 8; i++) {

            exec.execute(new Runnable() {

                @Override
                public void run() {

                    long timer = System.currentTimeMillis();

                    try {
                        Thread.sleep(new Random().nextInt(30000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        // 计数器加1
                        countDownLatch.countDown();
                    }

                    System.out.println(Thread.currentThread().getId() + " execute cost: " + (System.currentTimeMillis() - timer) + "ms");

                }

            });

        }

        // 等待计数器触发后继续往下走
        countDownLatch.await();

        System.out.println("all execute over, total cost: " + (System.currentTimeMillis() - timerAll));

    }

}
5. 多线程栅栏实战之 CyclicBarrier

CyclicBarrier 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。 CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。

public class CyclicBarrierTest {

    public static void main(String args[]) {

        ExecutorService exec = Executors.newCachedThreadPool();

        final CyclicBarrier barrier = new CyclicBarrier(4, new Runnable() {

            @Override
            public void run() {
                System.out.println("大家都到齐了,会议开始");
            }

        });

        for (int i = 0; i < 4; i++) {
            exec.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep((long) Math.random() * 15000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + "到了");
                    try {
                        barrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        exec.shutdown();
    }
}

CountDownLatch和CyclicBarrier区别:

CountDownLatch:一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行。 CyclicBarrier:N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。

这样应该就清楚一点了,对于CountDownLatch来说,重点是那个“一个线程”, 是它在等待, 而另外那N的线程在把“某个事情”做完之后可以继续等待,可以终止。而对于CyclicBarrier来说,重点是那N个线程,他们之间任何一个没有完成,所有的线程都必须等待。

CountDownLatch是计数器, 线程完成一个就记一个, 就像 报数一样, 只不过是递减的。而CyclicBarrier更像一个水闸, 线程执行就想水流, 在水闸处都会堵住, 等到水满(线程到齐)了, 才开始泄流。

6. 并发编程实战之信号量机制 Semaphore

Semaphore维护了当前访问的个数,提供同步机制,控制同时访问的个数,超过限制排队等待。

public class SemaphoreTest {

    private static final Semaphore semaphore = new Semaphore(2);

    public static void main(String[] args) {
        System.out.println(Runtime.getRuntime().availableProcessors());
        for(int i=0; i<8; i++){
            new Thread(new Runnable(){
                @Override
                public void run() {
                    try {
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName() + " acquired.");
                        Thread.sleep((long)(Math.random() * 2000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        semaphore.release();
                    }

                }
            }).start();

        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值