线程知识学习七——并发(辅助类)

继其几篇博客线程的并发知识的了解,我们使用了CountDownLatch和CyclicBarrier两个辅助类,这篇博客说实现线程并发其他辅助类。

Semaphore

Semaphore:信号量,就是资源数。网上看到的一个例子,说的很形象:
以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。
抽象的来讲,信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的线程/进程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。当一个线程调用Wait操作时,它要么得到资源然后将信号量减一,要么一直等下去(指放入阻塞队列),直到信号量大于等于一时。Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为释放了由信号量守护的资源。
这下明白了吧。
看示例代码:

import java.util.concurrent.Semaphore;
class SemaphoreDemo {
    /** * Semaphore:信号量 * 当前在多线程环境下被扩放使用,操作系统的信号量是个很重要的概念,在进程控制方面都有应用。 * Java 并发库 的Semaphore 可以很轻松完成信号量控制, * Semaphore可以控制某个资源可被同时访问的个数, * 通过 acquire() 获取一个许可,如果没有就等待, * 通过 release() 释放一个许可。 * 比如在Windows下可以设置共享文件的最大客户端访问个数。 */
    static void test(int threadNum) {
// Semaphore semaphore = new Semaphore(threadNum, true);// 是否公平

        final Semaphore semaphore = new Semaphore(3);//最多同时允许 3 个线程并发访问

        for (int i = 0; i < threadNum; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("线程" + Thread.currentThread().getName() + " 想要处理,剩余凭证:" + semaphore.availablePermits());
                        semaphore.acquire();
                        Thread.sleep((long) (Math.random() * 2000));
                        System.out.println("线程" + Thread.currentThread().getName() + " 处理完毕,归还凭证!");
                        // 如果注释下面这行,表示不释放凭证,那么后续线程将永远阻塞在semaphore.acquire();上
                        semaphore.release();// 用完后要释放,以给其它线程使用
                    } catch (Exception e) {}
                }
            }).start();
        }
    }
}
public class Test {
    public static void main(String[] args) {
        SemaphoreDemo.test(5);
    }
}

Phaser

首先来说一下Phaser用来解决的问题,情况是这样子的:
在多线程开发中,会碰到将多个任务分配给多个线程,每个线程执行他的任务,但是,每个任务又分为好几个阶段,每个阶段期望各个线程同时达到,意思是,每一步每个线程都要同步,当有一个线程走完第一步的时候,他得等待其他的线程都完成第一步了才能继续下一步,步调一致能解决很多问题。这就是Phaser要解决的问题。来我们开代码。

需求是这样的
开启3个线程,分别打印字母a,b,c各10次,然后进入下一阶段打印后面的字母d,e,f各10次,然后再进入下一阶段…….以此类推,直到整个字母表全部打印完毕。

import java.util.concurrent.Phaser;

public class MyTest {

    public static void main(String[] args) {
        Phaser phaser = new Phaser(3) {// 共有3个工作线程,因此在构造函数中赋值为3
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                System.out.println("\n=========华丽的分割线=============");
                return registeredParties == 0;
            }
        };
        System.out.println("程序开始执行");
        char a = 'a';
        for (int i = 0; i < 3; i++) { // 创建并启动3个线程
            new MyThread((char) (a + i), phaser).start();
        }

        while (!phaser.isTerminated()) {// 只要phaser不终结,主线程就循环等待
            Thread.yield();
        }
        System.out.println("程序结束");
    }
}

class MyThread extends Thread {
    private char c;
    private Phaser phaser;

    public MyThread(char c, Phaser phaser) {
        this.c = c;
        this.phaser = phaser;
    }

    @Override
    public void run() {
        while (!phaser.isTerminated()) {
            for (int i = 0; i < 10; i++) { // 将当前字母打印10次
                System.out.print(c + " ");
            }
            // 打印完当前字母后,将其更新为其后第三个字母,例如b更新为e,用于下一阶段打印
            c = (char) (c + 3);
            if (c > 'z') {
                // 如果超出了字母z,则在phaser中动态减少一个线程,并退出循环结束本线程
                phaser.arriveAndDeregister();
                break;
            } else {
                // 反之,等待其他线程到达阶段终点,再一起进入下一个阶段
                phaser.arriveAndAwaitAdvance();
            }
        }
    }
}

Exchanger

在多线程中,两个线程之间交换数据是非常常见的情况,我们可以使用公共的数据结构来实现,同样,Java也提供了多线程辅助类供我们使用,那就是Exchanger类,这个类可以帮助我们在两个线程之间同步数据结构,下面我们以这个类再来实现经典模式:生产者消费者模型。

import java.util.concurrent.Exchanger;  

/** * Exchanger让两个线程交换信息。 * 例子中服务生线程往空的杯子里倒水,顾客线程从装满水的杯子里喝水, * 然后通过Exchanger双方互换杯子,服务生接着往空杯子里倒水,顾客接着喝水, * 然后交换,如此周而复始。 */  
public class ExchangerTest {  

    // 描述一个装水的杯子 
    public static class Cup{  
        // 标识杯子是否有水 
        private boolean full = false;  
        public Cup(boolean full){  
            this.full = full;  
        }  
        // 添水,假设需要5s 
        public void addWater(){  
            if (!this.full){  
                try {  
                    Thread.sleep(5000);  
                } catch (InterruptedException e) {  
                }  
                this.full = true;  
            }  
        }  
        // 喝水,假设需要10s 
        public void drinkWater(){  
            if (this.full){  
                try {  
                    Thread.sleep(10000);  
                } catch (InterruptedException e) {  
                }  
                this.full = false;  
            }  
        }  
    }  

    public static void testExchanger() {  
        // 初始化一个Exchanger,并规定可交换的信息类型是杯子 
        final Exchanger<Cup> exchanger = new Exchanger<Cup>();  
        // 初始化一个空的杯子和装满水的杯子 
        final Cup initialEmptyCup = new Cup(false);   
        final Cup initialFullCup = new Cup(true);  

        //服务生线程 
        class Waiter implements Runnable {  
            public void run() {  
                Cup currentCup = initialEmptyCup;  
                try {  
                    int i=0;  
                    while (i < 2){  
                        System.out.println("服务生开始往杯子中添水:"  
                                + System.currentTimeMillis());  
                        // 往空的杯子里加水 
                        currentCup.addWater();  
                        System.out.println("服务生添水完毕:"  
                                + System.currentTimeMillis());  
                        // 杯子满后和顾客的空杯子交换 
                        System.out.println("服务生等待与顾客交换杯子:"  
                                + System.currentTimeMillis());  
                        currentCup = exchanger.exchange(currentCup);  
                        System.out.println("服务生与顾客交换杯子完毕:"  
                                + System.currentTimeMillis());  
                        i++;  
                    }  

                } catch (InterruptedException ex) {  
                }  
            }  
        }  

        //顾客线程 
        class Customer implements Runnable {  
            public void run() {  
                Cup currentCup = initialFullCup;  
                try {  
                    int i=0;  
                    while (i < 2){  
                        System.out.println("顾客开始喝水:"  
                                + System.currentTimeMillis());  
                        //把杯子里的水喝掉 
                        currentCup.drinkWater();  
                        System.out.println("顾客喝水完毕:"  
                                + System.currentTimeMillis());  
                        //将空杯子和服务生的满杯子交换 
                        System.out.println("顾客等待与服务生交换杯子:"  
                                + System.currentTimeMillis());  
                        currentCup = exchanger.exchange(currentCup);  
                        System.out.println("顾客与服务生交换杯子完毕:"  
                                + System.currentTimeMillis());  
                        i++;  
                    }  
                } catch (InterruptedException ex) {  
                }  
            }  
        }  

        new Thread(new Waiter()).start();  
        new Thread(new Customer()).start();  
    }  

    public static void main(String[] args) {  
        ExchangerTest.testExchanger();  
    }  
}  

宏观图

线程知识学习七——并发(辅助类)_第1张图片

你可能感兴趣的:(并发,线程)