同步器

线程之间相互合作时,需要用到同步器来完成同步,下面介绍几种常用同步器:

1.Semaphore(信号量)
信号量数量限制了访问资源的线程总数,线程请求会消耗一个信号量,当信号量为0时,新的线程会阻塞,直到有线程释放了一个信号量

线程类:

package Semaphore;

import java.util.concurrent.Semaphore;

public class SemaphoreThread extends Thread{

    Semaphore semaphore;
    public SemaphoreThread(Semaphore semaphore){
        this.semaphore = semaphore;
    }
    @Override
    public void run() {
        try {
            semaphore.acquire();
            System.out.println("一个线程正在执行");
            sleep(3000);
            System.out.println("一个线程结束运行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        semaphore.release();
    }
}

测试类:

package Semaphore;

import java.util.concurrent.Semaphore;

public class MainClass {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        SemaphoreThread t1 = new SemaphoreThread(semaphore);
        SemaphoreThread t2 = new SemaphoreThread(semaphore);
        SemaphoreThread t3 = new SemaphoreThread(semaphore);
        SemaphoreThread t4 = new SemaphoreThread(semaphore);
        SemaphoreThread t5 = new SemaphoreThread(semaphore);
        SemaphoreThread t6 = new SemaphoreThread(semaphore);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }
}

运行结果:

一个线程正在执行
一个线程正在执行
一个线程正在执行
一个线程结束运行
一个线程结束运行
一个线程正在执行
一个线程正在执行
一个线程结束运行
一个线程正在执行
一个线程结束运行
一个线程结束运行
一个线程结束运行

Process finished with exit code 0

可以看到,由于我们把信号量数量设置为3,所以最多只能有3个线程在同时执行

2.CountDownLatch(倒计时门栓)
每完成一次特定操作,倒计时减一,线程集需要等到倒计时为0时才可执行。注意倒计时门栓是一次性的,计数为0后就不能再用了

线程类:

package CountDownLatch;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchThread extends Thread {
    CountDownLatch countDownLatch;
    public CountDownLatchThread(CountDownLatch countDownLatch){
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        System.out.println("一个线程正在等待");
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("一个线程结束等待");
    }
}

测试类:

package CountDownLatch;

import java.util.concurrent.CountDownLatch;

public class MainClass {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(3);
        CountDownLatchThread t1 = new CountDownLatchThread(countDownLatch);
        CountDownLatchThread t2 = new CountDownLatchThread(countDownLatch);
        CountDownLatchThread t3 = new CountDownLatchThread(countDownLatch);
        t1.start();
        t2.start();
        t3.start();
        try {
            System.out.println("计数减一");
            Thread.sleep(1000);
            countDownLatch.countDown();
            System.out.println("计数减一");
            Thread.sleep(1000);
            countDownLatch.countDown();
            System.out.println("计数减一");
            Thread.sleep(1000);
            countDownLatch.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

计数减一
一个线程正在等待
一个线程正在等待
一个线程正在等待
计数减一
计数减一
一个线程结束等待
一个线程结束等待
一个线程结束等待

Process finished with exit code 0

可以看到,由于我们把计时器设置为3,所以需要经过三次计数减一操作之后,线程集才可执行

3.CyclicBarrier(循环障栅)
线程在障栅处等待,当等待线程凑够一定数量后,打开障栅,放行这些线程,并执行阶段处理方法,然后重新关闭障栅

线程类:

package CyclicBarrier;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierThread extends Thread {
    CyclicBarrier cyclicBarrier;

    public CyclicBarrierThread(CyclicBarrier cyclicBarrier) {
        this.cyclicBarrier = cyclicBarrier;
    }

    @Override
    public void run() {
        System.out.println("正在等待");

        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println("开始执行");

    }
}

阶段处理方法:

package CyclicBarrier;

public class Deal implements Runnable {
    @Override
    public void run() {
        System.out.println("打开障栅");
    }
}

测试类:

package CyclicBarrier;

import java.util.concurrent.CyclicBarrier;

public class MainClass {


    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Deal());
        CyclicBarrierThread t1 = new CyclicBarrierThread(cyclicBarrier);
        CyclicBarrierThread t2 = new CyclicBarrierThread(cyclicBarrier);
        CyclicBarrierThread t3 = new CyclicBarrierThread(cyclicBarrier);
        CyclicBarrierThread t4 = new CyclicBarrierThread(cyclicBarrier);
        CyclicBarrierThread t5 = new CyclicBarrierThread(cyclicBarrier);

        try {
            t1.start();
            Thread.sleep(2000);
            t2.start();
            Thread.sleep(2000);
            t3.start();
            Thread.sleep(2000);
            t4.start();
            Thread.sleep(2000);
            t5.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

运行结果:

正在等待
正在等待
打开障栅
开始执行
开始执行
正在等待
正在等待
打开障栅
开始执行
开始执行
正在等待

可以看到,由于我们把障栅大小设置为2,所以当等待线程到达2个时就打开障栅放行。由于我们只有5个线程,所以最后一个线程会一直在障栅处等待

4.Phaser(阶段器)
在循环障栅的基础上增加了控制器,可以在循环若干个阶段后拆除障栅。线程需要注册阶段器,当请求等待线程数等于注册线程数时,打开障栅,执行阶段处理方法,阶段处理方法返回true时拆除障栅。

线程类1:

package Phaser;

import java.util.concurrent.Phaser;

public class Thread1 extends Thread {
    Phaser phaser;

    public Thread1(Phaser phaser) {
        this.phaser = phaser;
        phaser.register();
    }

    @Override
    public void run() {
        System.out.println("等待中");
        phaser.arriveAndAwaitAdvance();
        System.out.println("等待中");
        phaser.arriveAndAwaitAdvance();
        System.out.println("注销");
        phaser.arriveAndDeregister();
    }
}

线程类2:

package Phaser;

import java.util.concurrent.Phaser;

public class Thread2 extends Thread {
    Phaser phaser;

    public Thread2(Phaser phaser) {
        this.phaser = phaser;
        phaser.register();
    }

    @Override
    public void run() {
        System.out.println("等待中");
        phaser.arriveAndAwaitAdvance();
        System.out.println("等待中");
        phaser.arriveAndAwaitAdvance();
        System.out.println("等待中");
        phaser.arriveAndAwaitAdvance();
        System.out.println("注销");
        phaser.arriveAndDeregister();
    }
}

测试类:

package Phaser;

import java.util.concurrent.Phaser;

public class MainClass {
    public static void main(String[] args) {
        Phaser phaser = new Phaser() {
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                try {
                    System.out.println("一个阶段完成,注册线程数量:" + registeredParties + ",第" + phase + "阶段");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return registeredParties == 1;
            }
        };

        Thread1 t1 = new Thread1(phaser);
        Thread2 t2 = new Thread2(phaser);

        t1.start();
        t2.start();
    }
}

运行结果:

等待中
等待中
一个阶段完成,注册线程数量:2,第0阶段
等待中
等待中
一个阶段完成,注册线程数量:2,第1阶段
注销
等待中
一个阶段完成,注册线程数量:1,第2阶段
注销

Process finished with exit code 0

可以看到只有等待线程数目等于注册线程数目时,才会打开障栅,完成一个阶段。由于我们的设置是当注册线程数为1时拆除障栅,所以第2阶段完成后就拆除了障栅,不会再有第3阶段

5.Exchanger(交换器)
两个线程之间交换数据,注意必须成对线程才可交换,如果有单个线程无法成对就会一直阻塞

线程一:

package Exchanger;

import java.util.concurrent.Exchanger;

public class Thread1 extends Thread {
    Exchanger<String> exchanger;
    public Thread1(Exchanger<String> exchanger){
        this.exchanger = exchanger;
    }

    @Override
    public void run() {
        System.out.println("线程1写入数据1");
        try {
            System.out.println("线程1获得数据:"+exchanger.exchange("1"));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程二:

package Exchanger;

import java.util.concurrent.Exchanger;

public class Thread2 extends Thread {
    Exchanger<String> exchanger;
    public Thread2(Exchanger<String> exchanger){
        this.exchanger = exchanger;
    }

    @Override
    public void run() {
        System.out.println("线程2写入数据2");
        try {
            sleep(2000);
            System.out.println("线程2获得数据:"+exchanger.exchange("2"));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程三:

package Exchanger;

import java.util.concurrent.Exchanger;

public class Thread3 extends Thread {
    Exchanger<String> exchanger;
    public Thread3(Exchanger<String> exchanger){
        this.exchanger = exchanger;
    }

    @Override
    public void run() {
        System.out.println("线程3写入数据3");
        try {
            System.out.println("线程3获得数据:"+exchanger.exchange("3"));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试类:

package Exchanger;

import java.util.concurrent.Exchanger;

public class MainClass {
    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<String>();

        Thread1 t1 = new Thread1(exchanger);
        Thread2 t2 = new Thread2(exchanger);
        Thread3 t3 = new Thread3(exchanger);

        t1.start();
        t2.start();
        t3.start();
    }
}

运行结果:

线程1写入数据1
线程2写入数据2
线程3写入数据3
线程3获得数据:1
线程1获得数据:3

由于我们人为让线程2休眠了2秒,所以线程1和线程3先准备好并交换了数据,线程2就一直处于阻塞状态

6.SynchronousQueue(同步队列)
当一个线程调用put方法时,它会一直阻塞直到另一个线程调用take方法,与Exchanger类似,但Exchanger数据双向传递,SynchronousQueue数据单向传递

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值