第三百二十三节 Java线程教程 - Java同步器

Java线程教程 - Java同步器

同步器对象与一组线程一起使用。

它维护一个状态,根据它的状态,它让一个线程通过或强迫它等待。

本节将讨论四种类型的同步器:

  • Semaphores
  • Barriers
  • Latches
  • Exchangers

信号量

信号量用于控制可以访问资源的线程数。

java.util.concurrent包中的Semaphore类表示信号量同步器。

您可以使用其构造函数创建信号量,如下所示:

final int MAX_PERMITS  = 3;
Semaphore  s = new Semaphores(MAX_PERMITS);

Semaphore类的另一个构造函数使用公平作为第二个参数

final int MAX_PERMITS  = 3;
Semaphore  s = new Semaphores(MAX_PERMITS,  true); // A  fair  semaphore

如果你创建一个公平的信号量,在多线程请求许可的情况下,信号量将保证先进先出(FIFO)。也就是说,首先请求许可的线程将首先获得许可。

要获取许可证,请使用acquire()方法。

如果许可证可用,它立即返回。

它阻止如果许可证不可用。线程在等待许可证可用时可能中断。

Semaphore类的其他方法允许您一次性获取一个或多个许可证。要释放许可证,请使用release()方法。

以下代码显示了一个Restaurant类,它使用信号量来控制对表的访问。

import java.util.Random;
import java.util.concurrent.Semaphore;class Restaurant {private Semaphore tables;public Restaurant(int tablesCount) {this.tables = new Semaphore(tablesCount);}public void getTable(int customerID) {try {System.out.println("Customer  #" + customerID+ "  is trying  to  get  a  table.");tables.acquire();System.out.println("Customer #" + customerID + "  got  a  table.");} catch (InterruptedException e) {e.printStackTrace();}}public void returnTable(int customerID) {System.out.println("Customer #" + customerID + "  returned a  table.");tables.release();}
}
class RestaurantCustomer extends Thread {private Restaurant r;private int customerID;private static final Random random = new Random();public RestaurantCustomer(Restaurant r, int customerID) {this.r = r;this.customerID = customerID;}public void run() {r.getTable(this.customerID); // Get a tabletry {int eatingTime = random.nextInt(30) + 1;System.out.println("Customer #" + this.customerID+ "  will eat for " + eatingTime + "  seconds.");Thread.sleep(eatingTime * 1000);System.out.println("Customer #" + this.customerID+ "  is done  eating.");} catch (InterruptedException e) {e.printStackTrace();} finally {r.returnTable(this.customerID);}}
}
public class Main{public static void main(String[] args) {Restaurant restaurant = new Restaurant(2);for (int i = 1; i <= 5; i++) {RestaurantCustomer c = new RestaurantCustomer(restaurant, i);c.start();}}
}

上面的代码生成以下结果。

障碍器

屏障使一组线在屏障点汇合。

来自到达屏障的组的线程等待,直到该组中的所有线程到达。

一旦组中的最后一个线程到达屏障,组中的所有线程都将被释放。

当你有一个可以分成子任务的任务时,你可以使用一个屏障;每个子任务可以在单独的线程中执行,并且每个线程必须在共同点处相遇以组合它们的结果。

java.util.concurrent包中的CyclicBarrier类提供了屏障同步器的实现。

CyclicBarrier类可以通过调用其reset()方法来重用。

以下代码显示了如何在程序中使用循环障碍。

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;class Worker extends Thread {private CyclicBarrier barrier;private int ID;private static Random random = new Random();public Worker(int ID, CyclicBarrier barrier) {this.ID = ID;this.barrier = barrier;}public void run() {try {int workTime = random.nextInt(30) + 1;System.out.println("Thread #" + ID + " is going to work for " + workTime + "  seconds");Thread.sleep(workTime * 1000);System.out.println("Thread #" + ID + " is waiting at the barrier.");this.barrier.await();System.out.println("Thread #" + ID + " passed the barrier.");} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {System.out.println("Barrier is broken.");}}}
public class Main {public static void main(String[] args) {Runnable barrierAction = () -> System.out.println("We are ready.");CyclicBarrier barrier = new CyclicBarrier(3, barrierAction);for (int i = 1; i <= 3; i++) {Worker t = new Worker(i, barrier);t.start();}}
}

上面的代码生成以下结果。

Phasers

Phaser提供类似于CyclicBarrier和CountDownLatch同步器的功能。它提供以下功能:

Phaser是可重复使用的。

在Phaser上同步的参与方数量可以动态更改。在循环障碍中,当创建障碍时,方的数量是固定的。

移相器具有相关的相位编号,从零开始。当所有注册方都到达移相器时,移相器进入下一个阶段,阶段编号加1。相位编号的最大值为Integer.MAX_VALUE。在其最大值之后,相位编号重新从零开始。

Phaser有终止状态。在终止状态的Phaser上调用的所有同步方法立即返回,而不等待提前。

移相器有三种类型的参与者计数:注册参与者计数,到达参与者计数和未参与方计数。

注册方数量是注册同步的方的数量。到达的当事方数目是已经到达移相器的当前阶段的各方的数目。

未携带者数量是尚未到达移动器的当前阶段的各方的数量。

当最后一方到达时,移相器前进到下一阶段。

或者,当所有注册方都到达移动器时,Phaser可以执行移相器操作。

CyclicBarrier允许您执行屏障操作,这是一个Runnable任务。

我们通过在Phaser类的onAdvance()方法中编写代码来指定移相器操作。

我们需要继承Phaser类,并覆盖onAdvance()方法以提供Phaser动作。

以下代码显示了如何表示通过在Phaser上同步启动的任务

import java.util.Random;
import java.util.concurrent.Phaser;class StartTogetherTask extends Thread {private Phaser phaser;private String taskName;private static Random rand = new Random();public StartTogetherTask(String taskName, Phaser phaser) {this.taskName = taskName;this.phaser = phaser;}@Overridepublic void run() {System.out.println(taskName + ":Initializing...");int sleepTime = rand.nextInt(5) + 1;try {Thread.sleep(sleepTime * 1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(taskName + ":Initialized...");phaser.arriveAndAwaitAdvance();System.out.println(taskName + ":Started...");}
}public class Main {public static void main(String[] args) {Phaser phaser = new Phaser(1);for (int i = 1; i <= 3; i++) {phaser.register();String taskName = "Task  #" + i;StartTogetherTask task = new StartTogetherTask(taskName, phaser);task.start();}phaser.arriveAndDeregister();}
}

上面的代码生成以下结果。

例子

以下代码显示了如何向Phaser添加Phaser Action。

import java.util.concurrent.Phaser;public class Main {public static void main(String[] args) {Phaser phaser = new Phaser() {protected boolean onAdvance(int phase, int parties) {System.out.println("Inside onAdvance(): phase  = " + phase+ ",  Registered Parties = " + parties);// Do not terminate the phaser by returning falsereturn false;}};// Register the self (the "main" thread) as a party phaser.register();System.out.println("#1: isTerminated():" + phaser.isTerminated());phaser.arriveAndDeregister();// Trigger another phase advancephaser.register();phaser.arriveAndDeregister();System.out.println("#2: isTerminated():" + phaser.isTerminated());phaser.forceTermination();System.out.println("#3: isTerminated():" + phaser.isTerminated());}
}

上面的代码生成以下结果。

例2

以下代码显示如何使用移相器生成一些整数。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Phaser;class AdderTask extends Thread {private Phaser phaser;private String taskName;private List<Integer> list;public AdderTask(String taskName, Phaser phaser, List<Integer> list) {this.taskName = taskName;this.phaser = phaser;this.list = list;}@Overridepublic void run() {do {System.out.println(taskName + "  added  " + 3);list.add(3);phaser.arriveAndAwaitAdvance();} while (!phaser.isTerminated());}
}public class Main {public static void main(String[] args) {final int PHASE_COUNT = 2;Phaser phaser = new Phaser() {public boolean onAdvance(int phase, int parties) {System.out.println("Phase:" + phase + ", Parties:" + parties+ ",  Arrived:" + this.getArrivedParties());boolean terminatePhaser = false;if (phase >= PHASE_COUNT - 1 || parties == 0) {terminatePhaser = true;}return terminatePhaser;}};List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());int ADDER_COUNT = 3;phaser.bulkRegister(ADDER_COUNT + 1);for (int i = 1; i <= ADDER_COUNT; i++) {String taskName = "Task  #" + i;AdderTask task = new AdderTask(taskName, phaser, list);task.start();}while (!phaser.isTerminated()) {phaser.arriveAndAwaitAdvance();}int sum = 0;for (Integer num : list) {sum = sum + num;}System.out.println("Sum = " + sum);}
}

上面的代码生成以下结果。

锁存器

锁存器使一组线程等待,直到它到达其终端状态。

一旦锁存器达到其终端状态,它允许所有线程通过。

与障碍不同,它是一个一次性的对象。它不能被重置和重用。

使用锁存器,其中在一定数量的一次性活动完成之前,多个活动不能进行。

例如,一个服务不应该启动,直到它依赖的所有服务都已启动。

java.util.concurrent包中的CountDownLatch类提供了一个锁存器的实现。

import java.util.concurrent.CountDownLatch;
class LatchHelperService extends Thread {private int ID;private CountDownLatch latch;public LatchHelperService(int ID, CountDownLatch latch) {this.ID = ID;this.latch = latch;}public void run() {try {Thread.sleep(1000);System.out.println("Service #" + ID + "  has  started...");} catch (InterruptedException e) {e.printStackTrace();} finally {this.latch.countDown();}}
}class LatchMainService extends Thread {private CountDownLatch latch;public LatchMainService(CountDownLatch latch) {this.latch = latch;}public void run() {try {System.out.println("waiting for services to start.");latch.await();System.out.println("started.");} catch (InterruptedException e) {e.printStackTrace();}}
}public class Main {public static void main(String[] args) {// Create a countdown latch with 2 as its counterCountDownLatch latch = new CountDownLatch(2);LatchMainService ms = new LatchMainService(latch);ms.start();for (int i = 1; i <= 2; i++) {LatchHelperService lhs = new LatchHelperService(i, latch);lhs.start();}}
}

上面的代码生成以下结果。

交换器

交换器允许两个线程在同步点处等待彼此。

当两个线程到达时,它们交换一个对象并继续他们的活动。

Exchanger类提供了交换器同步器的实现。

以下代码显示将使用交换器与客户交换数据的生产者线程。

import java.util.ArrayList;
import java.util.concurrent.Exchanger;class ExchangerProducer extends Thread {private Exchanger<ArrayList<Integer>> exchanger;private ArrayList<Integer> buffer = new ArrayList<Integer>();public ExchangerProducer(Exchanger<ArrayList<Integer>> exchanger) {this.exchanger = exchanger;}public void run() {while (true) {try {System.out.println("Producer.");Thread.sleep(1000);fillBuffer();System.out.println("Producer has produced and waiting:" + buffer);buffer = exchanger.exchange(buffer);} catch (InterruptedException e) {e.printStackTrace();}}}public void fillBuffer() {for (int i = 0; i <= 3; i++) {buffer.add(i);}}
}class ExchangerConsumer extends Thread {private Exchanger<ArrayList<Integer>> exchanger;private ArrayList<Integer> buffer = new ArrayList<Integer>();public ExchangerConsumer(Exchanger<ArrayList<Integer>> exchanger) {this.exchanger = exchanger;}public void run() {while (true) {try {System.out.println("Consumer.");buffer = exchanger.exchange(buffer);System.out.println("Consumer  has received:" + buffer);Thread.sleep(1000);System.out.println("eating:"+buffer);buffer.clear();} catch (InterruptedException e) {e.printStackTrace();}}}  
}
public class Main {public static void main(String[] args) {Exchanger<ArrayList<Integer>> exchanger = new Exchanger<>();ExchangerProducer producer = new ExchangerProducer(exchanger);ExchangerConsumer consumer = new ExchangerConsumer(exchanger);producer.start();consumer.start();}
}

上面的代码生成以下结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/16409.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

HTB:Active[WriteUP]

目录 连接至HTB服务器并启动靶机 1.How many SMB shares are shared by the target? 使用nmap对靶机TCP端口进行开放扫描 2.What is the name of the share that allows anonymous read access? 使用smbmap通过SMB服务对匿名用户共享权限情况进行扫描 3.Which file has…

vue3【组件封装】S-icon 图标 ( 集成 iconify )

1. 安装依赖 npm i -D iconify/jsonnpm i --save-dev iconify/vue2. 组件封装 src/components/S-icon.vue <script setup lang"ts"> // 搜索图标 https://icon-sets.iconify.design/ import { Icon } from iconify/vue defineProps({icon: {type: String,requ…

AI视觉小车基础--1.开发前的准备

1. 结束开机大程序 为了能够方便体验小车的APP功能&#xff0c;系统中增加了一个程序&#xff0c;此程序集合了APP的控制功能和玩法&#xff0c;所以称做“大程序”&#xff0c;而且在主板系统开机时&#xff0c;此程序会自动启动&#xff0c;所以称做“开机自启动大程序”。 开…

6.传输层协议、ACL

TCP和UDP协议 TCP/IP协议组的传输层协议 TCP(Transmission Control Protocol ) 传输控制协议 UDP&#xff08;User Datagram Protocol &#xff09; 用户数据报协议 TCP协议 TCP是面向连接的、可靠的进程到进程通信的协议 TCP提供全双工服务&#xff0c;即数据可在同一时间双…

避免数据丢失!在NAS上保存Docker容器配置的正确姿势

引言 如果你使用NAS来管理家庭或小型企业的数据,那么Docker容器一定不陌生。它能快速部署各种应用,比如Jellyfin、Plex等多媒体服务器。然而,很多人却踩过一个坑:NAS关机重启后,Docker容器的配置居然丢了!辛苦搭建的环境瞬间化为乌有。别担心,今天就来分享一套实用的技…

【Java的动态代理】

Java中有两种实现动态代理的方式jdk动态代理和CGLIB动态代理 jdk动态代理: 基于接口的动态代理, 目标对象必须实现接口cglib动态代理: 基于字节生成技术(ASM代码生成库), 能在运行时对java类和接口进行扩展实现 那么动态代理这个技术到底能帮我们干啥? 个人感觉这个技术在框…

python怎么安装numpy

1、在python官网https://pypi.python.org/pypi/numpy中找到安装的python版本对应的numpy版本。 例如&#xff1a; python版本是&#xff1a; 下载的对应numpy版本是&#xff1a; 2、将numpy下载到python的安装目录下的scripts文件夹中&#xff1b; 3、然后在cmd中执行以下命…

计算机三级 数据库技术

第一章 数据库应用系统开发方法 1.1 数据库应用系统生命周期 软件工程:软件工程的思想&#xff0c;即用工程的概念、原理、技术和方法对软件生产、开发的全过程进行跟踪和管理 软件开发方法:瀑布模型、快速原型模型、螺旋模型 DBAS生命周期模型 1.2 规划与分析 系统规划与定…

网络编程套接字2

之前我们已经介绍了UDP套接字流程&#xff0c;接下来我们介绍TCP流套接字编程&#xff0c;TCP的一个核心特点&#xff0c;面向字节流&#xff0c;读写数据的基本单位就是字节。 1.API介绍 1.1ServerSocket:是创建TCP服务器Socket的API&#xff08;专门给服务器用&#xff09;…

偌依-防重复提交

其中的使用工具类可去偌依的代码中查找 需要配合 springboot自定义过滤器构建可重复读取inputStream的request&#xff08;来源若依&#xff09; springboot自定义过滤器构建可重复读取inputStream的request&#xff08;来源若依&#xff09;-CSDN博客 定义注解 package co…

3D 数组插值 MATLAB

插值是一种根据现有数据点创建的趋势查找查询数据点值的方法。MATLAB 提供了许多选项来对 N 维数据执行插值。 在本文中&#xff0c;我们将讨论如何借助一些示例在 3D 数组中插入数据。我们将使用 MATLAB 的 interpn&#xff08;&#xff09; 函数来执行插值。 语法 vq interp…

如何在Typora中绘制流程图

如何在Typora中绘制流程图 在撰写文档时&#xff0c;清晰的流程图能极大地提升信息传递的效率。Typora是一款优秀的Markdown编辑器&#xff0c;支持通过Mermaid语法快速绘制流程图。本文将介绍如何在Typora中创建和自定义流程图&#xff0c;帮助你用更直观的方式呈现逻辑结构和…

SpringBoot集成Redis(全流程详解)

前言 通过在SpringBoot中集成Redis&#xff0c;详细梳理集成过程。包括SpringBoot启动过程中&#xff0c;容器的刷新、自动配置的流程、各类注解的处理。 类比在纯Spring中集成Redis&#xff0c;体验SpringBoot自动配置给开发带来了哪些便利。 一、测试样例 1.1配置文件 a…

机器人控制技术、传感器技术、Wi-Fi无线通信技术、AI视觉应用教学和实训: 智能小车车臂教学平台

1、基本介绍 智能车臂教学平台在硬件上采用模块化设计&#xff0c;主控板、运动车体、机械臂、各类传感器等都可以进行拆卸操作&#xff1b;在接口上&#xff0c;采用标准拔插式设计&#xff0c;减少接线&#xff0c;方便组装。使用Wi-Fi与控制软件进行通信&#xff0c;支持遥…

ssm113ssm框架的购物网站+vue(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;网上超市系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本网上超市系统就是在这…

C++ 里面散发的咖喱味儿 - Currying函数式编程

C 里面散发的咖喱味儿 - Currying函数式编程 大家好&#xff0c;最近几篇都在聊C里面的函数式编程&#xff0c;今天我们继续就某一个点来深入聊一下&#xff0c;来聊聊在 C 中如何使用 std::bind 来实现函数式编程&#xff0c;尤其是柯里化&#xff08;Currying&#xff09;这…

【Gitee版】一篇教你如何快速入门git(详解)

前言--区分Git与Gitee Git 是一个强大的分布式版本控制系统&#xff0c;用于管理源代码。市面上有很多基于git的仓库网站&#xff0c;例如&#xff1a;GitHub、Gitee、GitCode等&#xff0c;它们之间的关系就好像是&#xff1a;git为基类&#xff0c;剩余为子类的样子。使用的…

Linux系统编程学习 NO.11——进程的概念(2)

谈谈进程的性质 进程的竞争性 由于CPU资源是稀缺的,进程数量是众多的。不可避免需要造成进程排队等待CPU资源的动作&#xff0c;内核的设计者为了让操作系统合理的去调度这这些进程&#xff0c;就产生了进程优先级的概念。设置合理的进程优先级能让不同进程公平的去竞争CPU资…

灵神 刷题DAY1

Python与java的刷题的区别 1. Python没有分号 2. Python不能return的时候赋值 3. Python没有小括号和花括号 4. Python的循环很奇怪&#xff0c;没有for(int i0;i<32;i)这种形式 而是直接用的是for i in range(n)这种 5. Python中没有 6. Python中没有&& 是an…