多线程编程——实战篇(一)

2023-10-29

多线程编程——实战篇(二)

多线程编程——实战篇(三)

多线程编程——实战篇(四)

  在进入实战篇以前,我们简单说一下多线程编程的一般原则。

  [安全性]是多线程编程的首要原则,如果两个以上的线程访问同一对象时,一个线程会损坏另一个线程的数据,这就是违反了安全性原则,这样的程序是不能进入实际应用的。

  安全性的保证可以通过设计安全的类和程序员的手工控制。如果多个线程对同一对象访问不会危及安全性,这样的类就是线程安全的类,在JAVA中比 如String类就被设计为线程安全的类。而如果不是线程安全的类,那么就需要程序员在访问这些类的实例时手工控制它的安全性。

  [可行性]是多线程编程的另一个重要原则,如果仅仅实现了安全性,程序却在某一点后不能继续执行或者多个线程发生死锁,那么这样的程序也不能作为真正的多线程程序来应用。

  相对而言安全性和可行性是相互抵触的,安全性越高的程序,可性行会越低。要综合平衡。

  [高性能] 多线程的目的本来就是为了增加程序运行的性能,如果一个多线程完成的工作还不如单线程完成得快。那就不要应用多线程了。

  高性能程序主要有以下几个方面的因素:

  数据吞吐率,在一定的时间内所能完成的处理能力。

  响应速度,从发出请求到收到响应的时间。

  容量,指同时处理雅致同任务的数量。

  安全性和可行性是必要条件,如果达到不这两个原则那就不能称为真正的多线程程序。而高性是多线程编程的目的,也可以说是充要条件。否则,为什么采用多线程编程呢?

[生产者与消费者模式]

  首先以一个生产者和消费者模式来进入实战篇的第一节。

  生产者和消费者模式中保护的是谁?

  多线程编程都在保护着某些对象,这些个对象是"紧俏资源",要被最大限度地利用,这也是采用多线程方式的理由。在生产者消费者模式中,我们要保护的是"仓库",在我下面的这个例子中,

就是桌子(table)。

   我这个例子的模式完全是生产者-消费者模式,但我换了个名字。厨师-食客模式,这个食堂中只有1张桌子,同时最多放10个盘子,现在有4个厨师做菜,每 做好一盘就往桌子上放(生产者将产品往仓库中放),而有6个食客不停地吃(消费者消费产品,为了说明问题,他们的食量是无限的)。

  一般而言,厨师200-400ms做出一盘菜,而食客要400-600ms吃完一盘。当桌子上放满了10个盘子后,所有厨师都不能再往桌子上放,而当桌子是没有盘子时,所有的食客都只好等待。

  下面我们来设计这个程序:

  因为我们不知道具体是什么菜,所以叫它food:

 class Food{}

  然后是桌子,因为它要有序地放而且要有序地取(不能两个食客同时争取第三盘菜),所以我们扩展LinkedList,或者你用聚合把一个LinkedList作为属性也能达到同样的目的,例子中我是用

继承,从构造方法中传入一个可以放置的最大值。

class Table extends java.util.LinkedList{
int maxSize;
public Table(int maxSize){
this.maxSize = maxSize;
}
}

现在我们要为它加两个方法,一是厨师往上面放菜的方法,一是食客从桌子上拿菜的方法。

放菜:因为一张桌子由多个厨师放菜,所以厨师放菜的要被同步,如果桌子上已经有十盘菜了。所有厨师就要等待:

 public synchronized void putFood(Food f){
while(this.size() >= this.maxSize){
try{
this.wait();
}catch(Exception e){}
}
this.add(f);
notifyAll();
}

拿菜:同上面,如果桌子上一盘菜也没有,所有食客都要等待:

 public synchronized Food getFood(){
while(this.size() <= 0){
try{
this.wait();
}catch(Exception e){}
}
Food f = (Food)this.removeFirst();
notifyAll();
return f;
}

厨师类:

  由于多个厨师要往一张桌子上放菜,所以他们要操作的桌子应该是同一个对象,我们从构造方法中将桌子对象传进去以便控制在主线程中只产生一张桌子。

厨师做菜要用一定的时候,我用在make方法中用sleep表示他要消耗和时候,用200加上200的随机数保证时间有200-400ms中。做好后就要往桌子上放。

这里有一个非常重要的问题一定要注意,就是对什么范围同步的问题,因为产生竞争的是桌子,所以所有putFood是同步的,而我们不能把厨师自己做菜的时 间也放在同步中,因为做菜是各自做的。同样食客吃菜的时候也不应该同步,只有从桌子中取菜的时候是竞争的,而具体吃的时候是各自在吃。所以厨师类的代码如 下:

 class Chef extends Thread{
Table t;
Random r = new Random(12345);
public Chef(Table t){
this.t = t;
}
public void run(){
while(true){
Food f = make();
t.putFood(f);
}
}
private Food make(){

try{
Thread.sleep(200+r.nextInt(200));
}catch(Exception e){}
return new Food();
}
}

同理我们产生食客类的代码如下:

class Eater extends Thread{
Table t;
Random r = new Random(54321);
public Eater(Table t){
this.t = t;
}
public void run(){
while(true){
Food f = t.getFood();
eat(f);
}
}
private void eat(Food f){

try{
Thread.sleep(400+r.nextInt(200));
}catch(Exception e){}
}
}

完整的程序在这儿:

package debug;
import java.util.regex.*;
import java.util.*;


class Food{}

class Table extends LinkedList{
int maxSize;
public Table(int maxSize){
this.maxSize = maxSize;
}
public synchronized void putFood(Food f){
while(this.size() >= this.maxSize){
try{
this.wait();
}catch(Exception e){}
}
this.add(f);
notifyAll();
}

public synchronized Food getFood(){
while(this.size() <= 0){
try{
this.wait();
}catch(Exception e){}
}
Food f = (Food)this.removeFirst();
notifyAll();
return f;
}
}


class Chef extends Thread{
Table t;
String name;
Random r = new Random(12345);
public Chef(String name,Table t){
this.t = t;
this.name = name;
}
public void run(){
while(true){
Food f = make();
System.out.println(name+" put a Food:"+f);
t.putFood(f);
}
}
private Food make(){
try{
Thread.sleep(200+r.nextInt(200));
}catch(Exception e){}
return new Food();
}
}

class Eater extends Thread{
Table t;
String name;
Random r = new Random(54321);
public Eater(String name,Table t){
this.t = t;
this.name = name;
}
public void run(){
while(true){
Food f = t.getFood();
System.out.println(name+" get a Food:"+f);
eat(f);

}
}
private void eat(Food f){

try{
Thread.sleep(400+r.nextInt(200));
}catch(Exception e){}
}
}

public class Test {
public static void main(String[] args) throws Exception{
Table t = new Table(10);
new Chef("Chef1",t).start();
new Chef("Chef2",t).start();
new Chef("Chef3",t).start();
new Chef("Chef4",t).start();
new Eater("Eater1",t).start();
new Eater("Eater2",t).start();
new Eater("Eater3",t).start();
new Eater("Eater4",t).start();
new Eater("Eater5",t).start();
new Eater("Eater6",t).start();

}
}

这一个例子中,我们主要关注以下几个方面:

  1.同步方法要保护的对象,本例中是保护桌子,不能同时往上放菜或同时取菜。

  假如我们把putFood方法和getFood方法在厨师类和食客类中实现,那么我们应该如此:

(以putFood为例)

class Chef extends Thread{
Table t;
String name;
public Chef(String name,Table t){
this.t = t;
this.name = name;
}
public void run(){
while(true){
Food f = make();
System.out.println(name+" put a Food:"+f);
putFood(f);
}
}
private Food make(){
Random r = new Random(200);
try{
Thread.sleep(200+r.nextInt());
}catch(Exception e){}
return new Food();
}
public void putFood(Food f){//方法本身不能同步,因为它同步的是this.即Chef的实例

synchronized (t) {//要保护的是t
while (t.size() >= t.maxSize) {
try {
t.wait();
}
catch (Exception e) {}
}
t.add(f);
t.notifyAll();
}
}
}

  2.同步的范围,在本例中是放和取两个方法,不能把做菜和吃菜这种各自不相干的工作放在受保护的范围中。

  3.参与者与容积比

   对于生产者和消费者的比例,以及桌子所能放置最多菜的数量三者之间的关系是影响性能的重要因素,如果是过多的生产者在等待,则要增加消费者或减少生产者的数据,反之则增加生产者或减少消费者的数量。

  另外如果桌子有足够的容量可以很大程序提升性能,这种情况下可以同时提高生产者和消费者的数量,但足够大的容时往往你要有足够大的物理内存。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多线程编程——实战篇(一) 的相关文章

随机推荐

  • leetcode 974. 和可被 K 整除的子数组

    给定一个整数数组 A 返回其中元素之和可被 K 整除的 连续 非空 子数组的数目 示例 输入 A 4 5 0 2 3 1 K 5 输出 7 解释 有 7 个子数组满足其元素之和可被 K 5 整除 4 5 0 2 3 1 5 5 0 5 0
  • 【3】Midjourney基本指令

    将自己的图片上传 AI训练 点击输入框的 号 会有上传文件 双击就可以选择图像上传 回车 图片上按右键 选复制链接 直接贴到输入框imagine prompt 后面 贴完后 先空一格 重要 再输入文本 可逗号分段 及参数 垫图不限只用1张图
  • FatFs文件系统配置多线程安全

    文件系统 FatFs 是用于小型嵌入式系统的通用 FAT exFAT 文件系统模块 FatFs 模块的编写符合 ANSI C C89 并与磁盘 I O 层完全分离 因此它独立于硬件平台 它可以集成到资源有限的小型微控制器中 例如 8051
  • 将cmd中的命令输出保存为txt文本文件

    在使用Windows XP中的cmd exe工具时 有时候我们想要把我们的输入命令及结果保存起来 我们可将命令运行的结果输出到文本文件 在你输入的命令后再加上 gt 和你想保存的文件地址和名字就可以了 例如 将Ping命令的运行结果输出到D
  • iOS开发系列--视图切换

    2014 08 28 08 27 by KenshinCui 105420 阅读 64 评论 收藏 编辑 概述 在iOS开发中视图的切换是很频繁的 独立的视图应用在实际开发过程中并不常见 除非你的应用足够简单 在iOS开发中常用的视图切换有
  • 集成灶排名十大品牌之一的火星人坚持科技创新,打破行业同质化“窘况”

    造型 千篇一律 油烟四窜 功能单一 是长期以来传统油烟机留给人们最直观的感觉 随着集成灶时代的来临 集造型时尚 吸油烟率出色 功能强大等优势于一身的集成灶被更多家庭选择 但是纵观近几年的集成灶十大排名榜 却也不难发现 市场中集成灶产品也出现
  • 泛型父类需要获取其子类定义的泛型类型即class对象方法

    获取泛型T的class对象 方法一 TypeToken 方法二 java lang reflect ParameterizedType Type demo 结果 方法一 TypeToken TypeToken 是google提供的一个解析J
  • vue:antV G2在vue中的使用(阿里图表,类似echarts)

    效果比echarts好看 官方文档 https g2 antv vision zh docs manual getting started 转载来源 https www jianshu com p a836ae8aeeb7 一 安装 npm
  • vue中将 后台返回的0,1等 代码转换成 男,女等汉字。

    vue中将 后台返回的0 1等 代码转换成 男 女等汉字 一 table表格中 只有一项或者两项时候 在table项 中 可以使用多项的方法 也可以使用 作用域插槽使用 二 table表格外部 的方法 多项的时候 例如 01 02 03 0
  • 层级型结构图

    WBS 工作分解结构 用来显示如何把项目可交付成果分解为工作包 有助于明确高层级的职责 OBS 组织分解结构 按照组织现有的部门 单元或团队排列 并在每个部门下列出项目活动或工作包 运营部门 信息技术或采购部 只需要找到其所在的OBS位置
  • R语言实战笔记--第十四章 主成分和因子分析

    R语言实战笔记 第十四章 主成分和因子分析 标签 空格分隔 R语言 主成分分析 因子分析 原理及区别 主成分分析与因子分析很接近 其目的均是为了降维 以更简洁的数据去解释结果 但这两种方法其实是相差甚远 有幸找到一篇博文描述了这两者的不同
  • 转1:Python字符编码详解

    Python27字符编码详解 声明 一 字符编码基础 1 抽象字符清单ACR 2 已编码字符集CCS 3 字符编码格式CEF 31 ASCII初创 311 ASCII 312 EASCII 32 MBCSDBCSANSI本地化 321 GB
  • Android Studio导入工程报错[Plugin with id 'com.android.application' not found]

    http blog csdn net seafishyls article details 53572939 版权声明 本文为博主原创文章 未经博主允许不得转载 目录 出错现象 原因分析 buildgradleProject xxxx bu
  • 【解决】android设备有root权限,执行“adb disable-verity”命令报错“verity cannot be disabled/enabled - USER build”

    这个错误提示意味着你的设备是使用USER版本的固件构建的 这个版本不允许禁用verity功能 但是 你可以尝试以下步骤来解决问题 确认你的设备是否已经root 可以通过执行 adb root 命令来检查 确认你的设备是否已经启用了开发者选项
  • vue-cli3.0 配置使用 less 全局样式

    在css样式文件夹中新增 global less 作为全局样式 在 main js 中通过 import 导入 可正常使用样式 但 global less 中定义的样式属性还不能全局使用 需通过以下方法配置 1 安装 style resou
  • 加密软件如何和PDM软件结合使用

    在企业里面都会用到加密软件 比如IP guard 壹加密 亿赛通等等 但是加密软件会导致PDM软件原本的各个功能都被限制 所以为了助力企业解决问题 更好的帮助企业完成PDM的正常使用 今天给大家分享一下解决方法 请看下文 SOLIDWORK
  • APP自动化测试-2. Appium录制测试用例

    APP自动化测试 2 Appium录制测试用例 文章目录 APP自动化测试 2 Appium录制测试用例 前言 一 android自动化依赖 1 adb依赖 2 android真机或模拟器 3 Appium Desktop Appium 二
  • CSS 鼠标样式和手指样式整理

    巧合要用到鼠标样式效果 就顺便整理了下十五种CSS鼠标样式 小例子供大家使用啊 CSS鼠标样式语法如下 任意标签中插入 style cursor 例 子 span 文本或其它页面元素 span a href 文本或其它页面元素 a 注意把
  • 【ReID】【代码注释】难样本三元组损失TriHard loss deep-person-reid/losses.py

    源码URL https github com michuanhaohao deep person reid blob master losses py TriHard loss部分的源码注释 class TripletLoss nn Mod
  • 多线程编程——实战篇(一)

    多线程编程 实战篇 二 多线程编程 实战篇 三 多线程编程 实战篇 四 在进入实战篇以前 我们简单说一下多线程编程的一般原则 安全性 是多线程编程的首要原则 如果两个以上的线程访问同一对象时 一个线程会损坏另一个线程的数据 这就是违反了安全