【java并发编程】信号模式

信号模式

​ 顾名思义,信号模式应该是通过发送信号进行并发处理的。没错!信号模式就是针对 一个线程向另一个线程发送信号 的情景出现的。线程1完成事件A时会发出信号,线程2运行到一定阶段时会等待检查线程1发出的事件A的完成信号,只有收到事件A的完成信号后线程2才可以继续运行。最简单直接的方法可以使用Object类的wait()和notify()方法实现

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author xuxi
 * @date 2021/8/3
 * @desciption 使用wait和notify
 **/
public class SignalPattern {

    public static void main(String[] args) {
        SignalObject signalObject = new SignalObject();
        AtomicInteger count = new AtomicInteger();
        Thread t1 = new Thread(() -> {
            System.out.println("t1线程已启动");
            synchronized (signalObject) {
                try {
                    System.out.println("t1开始等待................t2执行之前" + count);
                    signalObject.wait();
                    System.out.println("t1接收到信号...........t2的执行结果是" + count);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread t2 = new Thread(() -> {
            System.out.println("t2线程已启动");
            synchronized (signalObject) {
                System.out.println("t2正在执行任务...........count 0->1");
                count.getAndIncrement();
                System.out.println("t2执行任务结束...........");
                System.out.println("t2传输完成信号...........");
                signalObject.notify();
            }
        });

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

//本质就是Object
class SignalObject {
}

​ 在这个实例代码中,使用了AtomicInteger类,官方的解释如下

An int value that may be updated atomically. See the java.util.concurrent.atomic package specification for description of the properties of atomic variables. An AtomicInteger is used in applications such as atomically incremented counters, and cannot be used as a replacement for an Integer. However, this class does extend Number to allow uniform access by tools and utilities that deal with numerically-based classes.

可以原子更新的int值。有关原子变量属性的描述,请参见java.util.concurrent.atomic包规范。原子整数用于诸如原子递增计数器之类的应用程序中,不能用作整数的替换。然而,该类确实扩展了数字,以允许处理基于数字的类的工具和实用程序统一访问。

​ 就是说在多线程的环境中,我们想让一个公共的整型变量进行自增,使用i++和++i是存在线程问题,这时候就需要使用AtomicInteger,用原子方式进行自增操作。

​ 除了直接使用Object的wait()和notify()方法以外,还可以使用ReentrantLock类或者Semaphore类

Semaphore类

​ 一个计数信号量。每个acquire()都会阻塞,直到许可证可用,然后才能使用它。每个release()添加许可证,潜在地释放阻塞获取方。

信号量通常用于限制线程数,而不是访问某些(物理或逻辑)资源。

​ 信号量被初始化为一个,并且被使用,使得它只有至多一个允许可用,可以用作互斥锁。 这通常被称为二进制信号量 ,因为它只有两个状态:一个许可证可用,或零个许可证可用。 当以这种方式使用时,二进制信号量具有属性(与许多Lock实现不同),“锁”可以由除所有者之外的线程释放(因为信号量没有所有权概念)。 这在某些专门的上下文中是有用的,例如死锁恢复。

​ 此类的构造函数可选择接受公平参数。 当设置为false时,此类不会保证线程获取许可的顺序。 特别是, 闯入是允许的,也就是说,一个线程调用acquire()可以提前已经等待线程分配的许可证-在等待线程队列的头部逻辑新的线程将自己。 当公平设置为真时,信号量保证调用acquire方法的线程被选择以按照它们调用这些方法的顺序获得许可(先进先出; FIFO)。 请注意,FIFO排序必须适用于这些方法中的特定内部执行点。 因此,一个线程可以在另一个线程之前调用acquire ,但是在另一个线程之后到达排序点,并且类似地从方法返回。 未定义的tryAcquire方法不符合公平性设置,但将采取任何可用的许可证。

​ 通常,用于控制资源访问的信号量应该被公平地初始化,以确保线程没有被访问资源。 当使用信号量进行其他类型的同步控制时,非正常排序的吞吐量优势往往超过公平性。

import java.util.concurrent.Semaphore;

/**
 * @author xuxi
 * @date 2021/8/4
 * @desciption 使用Semaphore类, 控制线程打印奇偶数
 **/
public class SignalPattern2 {
    public static void main(String[] args) {
        SignalObject2 signalObject_1 = new SignalObject2();
        SignalObject2 signalObject_2 = new SignalObject2();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i <= 100; i++) {
                if (i % 2 == 1) {
                    System.out.println("奇数线程t1 :" + i);
                    signalObject_1.doSignal();
                } else {
                    signalObject_2.doWait();
                }
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i <= 100; i++) {
                if (i == 50) {
                    //打断t2线程,会报InterruptedException
                    Thread.currentThread().interrupt();
                }
                if (i % 2 == 0) {
                    System.out.println("偶数线程t2 :" + i);
                    signalObject_2.doSignal();
                } else {
                    signalObject_1.doWait();
                }
            }
        });
        t1.start();
        t2.start();
    }
}

class SignalObject2 {
    Semaphore semaphore;

    //初始化许可证的数量
    public SignalObject2(int permits) {
        semaphore = new Semaphore(permits);
    }

    public SignalObject2() {
        this(0);
    }

    public void doSignal() {
        //当前线程释放1个可用的许可证。
        semaphore.release();
    }

    public void doWait() {
        try {
            //当前线程尝试去阻塞的获取1个许可证。
            //此过程是阻塞的,它会一直等待许可证,直到发生以下任意一件事:
            //当前线程获取了1个可用的许可证,则会停止等待,继续执行。
            //当前线程被中断,则会抛出InterruptedException异常,并停止等待,继续执行。
            semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
ReentrantLock类

ReentrantLock使用起来比较简单,就是一个加锁解锁

Lock lock = new ReenTrantLock(true);
try {
    lock.lock(); // 加锁
    //操作
} finally {
    lock.unlock(); // 解锁
}

在初始化的时候可以选择公平锁和不公平锁

new ReenTrantLock(true)是公平锁,new ReenTrantLock(false)是不公平锁

区别:
  • 公平锁是按照加锁顺序来的。先lock先获得锁
  • 非公平锁是不按顺序的。先lock不一定先获得锁
信号模式代码实现
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author xuxi
 * @date 2021/8/5
 * @desciption
 **/
public class SinglePattern3 {


    public static void main(String[] args) {
        SignalObj signalObj = new SignalObj();
        Thread t1 = new Thread(() -> {
            synchronized (signalObj) {
                signalObj.signal("t1");
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (signalObj) {
                signalObj.signal("t2");

            }
        });
        Thread t3 = new Thread(() -> {
            synchronized (signalObj) {
                signalObj.signal("t3");

            }
        });
        Thread t4 = new Thread(() -> {
            synchronized (signalObj) {
                signalObj.signal("t4");

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

class SignalObj {
    //开启公平模式
    final Lock lock = new ReentrantLock(true);
    AtomicInteger atomicInteger = new AtomicInteger();

    public void signal(String threadName) {
        try {
            lock.lock();
            System.out.println(threadName + "已经锁住,正在处理业务......");
            atomicInteger.getAndIncrement();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            System.out.println(threadName + "业务执行完毕 : " + atomicInteger + " 已经解锁");
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值