`
clearity
  • 浏览: 35470 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

获得更好并发和线程安全性的场景和解决方案--第三部分 信号量和互斥锁

阅读更多

场景1:web服务通过http请求数据,将请求对象放在内部的队列中,并且有一个单独的工作线程从队列中取请求对象进行处理.比如,一个交易系统创建一个购买订单并放入队列中,这时一个独立的消费线程获取订单信息并且将发送给证券交易所,这是一个典型的生产者消费者场景.

 

场景2:如果之前的条件未知信号量可以用来创建工作线程,这是因为信号量可以在0许可的情况下创建,并在一系列的释放以后运行.比如,如果你想递归的遍历内部文件夹并利用生成的若干线程将html类型的文件转移到目标文件夹中,然后可以通过调用release方法调用递增许可数量, 单独的处理线程需要在开始处理这些文件之前获取足够的许可才可以进行处理.

 

场景3:当需要对共享资源进行递增或者递减的引用计数时,递增、递减或者测试的操作必须是线程安全的。比如,一个用来在用户登录时递增或者在用户登出时递减当前活动用户的计数器.

 

方案:通常情况下,信号量是根据字母顺序代码的某个位置通过持有判断标识来发送消息的实现。在程序设计中,特别是在Unix系统中,信号量是在多个进程竞争相同操作系统资源的时候用来协作或者同步活动的技术。信号量通畅被用于两个目的:共享内存空间和共享文件访问。在java中,信号量是用来对多线程进行协作。

 

Q:互斥锁和信号量之间的区别是什么?

A:互斥锁:好比厕所的钥匙一样。同一时间只能有一个人拥有并且独占厕所。当一个人使用过后,持有钥匙的人会将钥匙传递给队列中的下一个人。在java中,每一个对象都有一个互斥锁并且只能有一个线程可以持有它。

 

   信号量:是一串厕所的钥匙。比如,厕所有三把锁,对应的钥匙有三个。在刚开始时,信号量的值被设置为3,随后当有人拿到钥匙进入厕所后值会递减。如果厕所满了,也就是没有钥匙省下的时候,信号量的值是0.现在,如果有一个人使用完厕所离开后,信号量会递增为1(获得了一个钥匙),并且将钥匙传递给了排队的下个人。

 

这里有使用互斥锁来实现生产者和消费者的例子.

 

public class ProducerConsumer {
 
 private int count;  //被多个线程共享访问的资源
 
 public synchronized void consume() {
  while (count == 0) {
   try {
    wait();    //等待唤醒
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
 
  //如果信号量的值大于0 ,自减
  count--;     //递减1
  System.out.println("Consumed:" + count);
  notify();  //唤醒等待线程恢复运行
 
 }
 
 public synchronized void produce() {
  while(count > 0) {
   try {
    wait();    //wait till notified
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
   
  //如果信号量的值等于0,递增
  count++;
  System.out.println("Produced: " + count);
  notify();   //唤醒等待线程恢复运行
 }
 
 public static void main(String[] args)  {
        //内部类只能访问final类型的变量
  final ProducerConsumer pc = new ProducerConsumer();
 
  //使用匿名内部类创建生成线程
  Runnable producer = new Runnable() {
   @Override
   public void run() {
    for (int i = 0; i < 3; i++) {
     pc.produce(); 
    }
   }
  };
 
   
  //匿名内部类创建消费线程
  Runnable consumer = new Runnable() {
   @Override
   public void run() {
    for (int i = 0; i < 3; i++) {
      pc.consume();
    }
   }
  };
 
  Thread producerThread = new Thread(producer); //主线程中创建新线程
  Thread consumerThread = new Thread(consumer); //主线程中创建新县城
 
  producerThread.start();  // 执行生产线程
  consumerThread.start();  // 执行消费线程
 
 }
}

 

 

现在,让我们来看下Java 5中的信号量的例子。你可以使用一个数量的许可创建信号量,并且可以获得和释放许可。

 

import java.util.concurrent.Semaphore;
 
public class ProducerConsumer2 {
 
 private int count;
  
 /**
  *  调用release方法递增许可
  *  调用acquire方法递减许可
  */
 static Semaphore semCon = new Semaphore(0);
 static Semaphore semProd = new Semaphore(1); //从一个许可开始
 
 public  void consume() {
  try {
   //继续执行前从信号量获取许可
   semCon.acquire();
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 
  count--;
  System.out.println("Consumed:" + count);
  //释放许可,返回给信号量
  semProd.release(); 
 
 }
 
 public void produce() {
  try {
   //执行前获得信号量许可
   semProd.acquire(); 
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  count++;
  System.out.println("Produced: " + count);
  //释放许可,归还给信号量
  semCon.release();   
 }
 
 public static void main(String[] args)  {
 
  final ProducerConsumer2 pc = new ProducerConsumer2();
 
  //匿名内部类生产线程
  Runnable producer = new Runnable() {
   @Override
   public void run() {
    for (int i = 0; i < 3; i++) {
     pc.produce(); 
    }
   }
  };
 
  //匿名内部类消费线程
  Runnable consumer = new Runnable() {
   @Override
   public void run() {
    for (int i = 0; i < 3; i++) {
      pc.consume();
    }
   }
  };
 
  Thread producerThread = new Thread(producer); //主线程创建新线程
  Thread consumerThread = new Thread(consumer); //主线程创建新县城
 
  producerThread.start(); // 启动生产者线程
  consumerThread.start(); // 启动消费者线程
 
 }
}

 

 

上面两端代码的输出:

 

Produced: 1
Consumed:0
Produced: 1
Consumed:0
Produced: 1
Consumed:0

 

分享到:
评论

相关推荐

    Python的互斥锁与信号量详解

    1. 互斥锁,是使用一把锁把代码保护起来,以牺牲性能换取代码的安全性,那么Rlock后 必须要relase 解锁 不然将会失去多线程程序的优势 2. 互斥锁的基本使用规则: import threading # 声明互斥锁 lock=threading....

    Java并发编程(学习笔记).xmind

    (1)二值信号量可用作互斥体(mutex) (2)实现资源池,例如数据库连接池 (3)使用信号量将任何一种容器变成有界阻塞容器 栅栏 能够阻塞一组线程直到某个事件发生 栅栏和闭锁的...

    并发编程面试题汇总.docx并发编程是指在一个程序中同时执行多个独立的任务或操作的能力 在面试中,常常会问到与并发编程相关的问题

    为了避免竞态条件,需要使用同步机制(如锁、信号量、互斥量等)来保证对共享资源的互斥访问。 死锁和活锁:死锁指的是多个线程相互等待对方释放资源,导致程序无法继续执行的情况。而活锁指的是线程不断尝试解决

    python线程安全及多进程多线程实现方法详解

    进程和线程的区别 进程是对运行时程序的封装,是系统资源调度和分配的基本单位 线程是进程的子任务,cpu调度和分配的基本单位... 互斥量(锁): 通过互斥机制防止多个线程同时访问公共资源。 信号量(Semphare):

    生产者-消费者模型模拟进程调度,带报告,课程设计

    2、用信号量机制解决进程(线程)的同步与互斥问题。 二、实验目的 1.掌握基本的同步互斥算法,理解生产者和消费者模型。 2.了解Windows 2000/XP中多线程的并发执行机制,线程间的同步和互斥。 3.学习使用Windows ...

    UNIX网络编程 卷2 进程间通信 带完整书签,完整目录

    第三部分 同步 125 第7章 互斥锁和条件变量 126 7.1 概述 126 7.2 互斥锁:上锁与解锁 126 7.3 生产者-消费者问题 127 7.4 对比上锁与等待 131 7.5 条件变量:等待与信号发送 132 7.6 条件变量:定时等待和...

    UNIX网络编程 卷2:进程间通信

     第三部分 同步  第7章 互斥锁和条件变量 126  7.1 概述 126  7.2 互斥锁:上锁与解锁 126  7.3 生产者-消费者问题 127  7.4 对比上锁与等待 131  7.5 条件变量:等待与信号发送 132  7.6 条件变量:定时...

    理解原子操作,CAS加锁是线程安全的.docx

    偏向锁、轻量级锁和重量级锁不同的地方在于不是通过信号量机制(强制阻塞)而是通过自旋CAS实现互斥访问的,避免了强制阻塞时用户态与核心态之间切换带来的开销(系统调用),这里的开销主要是保存用户态的上下文...

    philosophers:餐饮哲学家问题的3种解决方案

    在本项目中,您将使用(1)线程和互斥量,(2)线程和信号量以及(3)进程和信号量,为餐饮哲学家问题实现3个解决方案。 餐饮哲学家有什么问题? 餐饮哲学家是一个关于共享资源和并发的经典计算机科学问题。 如果...

    《UNIX网络编程 第2版. 第2卷, 进程间通信(中文版)》(W·Richard Stevens[美] 著)

    第三部分 同步 第7章 互斥锁和条件变量 126 7.1 概述 126 7.2 互斥锁:上锁与解锁 126 7.3 生产者-消费者问题 127 7.4 对比上锁与等待 131 7.5 条件变量:等待与信号发送 132 7.6 条件变量:定时等待和广播 136 7.7 ...

    UNIX网络编程 第2卷 进程间通信

    第三部分 同步 第7章 互斥锁和条件变量 126 7.1 概述 126 7.2 互斥锁:上锁与解锁 126 7.3 生产者-消费者问题 127 7.4 对比上锁与等待 131 7.5 条件变量:等待与信号发送 132 7.6 条件变量:定时等待和广播 136 7.7 ...

    5操作系统实验报告.doc

    程,熟练掌握线程的创建pthread_create(),线程终止pthread_exit(),等待线程合 并pthread_join()等线程控制的操作,利用信号量或者互斥锁实现线程间的同步。 二、实验内容 问题:求 100000 个浮点数(精确小数点右 ...

    深入浅出Linux设备驱动之并发控制

    在驱动程序中,当多个线程同时访问相同的资源时(驱动程序中的全局变量是一种典型的共享资源),可能会引发\"竞态\",因此...Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)。

    网络编程教程,很好的一本写linux网络编程书,这是我上传的源码

     15.2.2 互斥锁、条件变量和条件信号  15.2.3 线程和信号  15.3 在网络程序中应用多线程  15.3.1 线程间参数的传递  15.3.2 线程安全函数的设计  15.3.3 多进程的并发服务器和多线程的并发...

    linux programming instances网络编程教程 附源代码

    第3章 传输层协议tcp和udp 3.1 tcp/ip基本框架 3.1.1 网络协议与层次 3.1.2 数据的封装与分用 3.1.3 客户-服务器模型 3.2 用户数据报协议(udp) 3.2.1 udp首部 3.3 传输控制协议(tcp) 3.3.1 顺序传输...

    C#多线程编程中的锁系统(三)

    本章主要说下基于内核模式构造的线程同步方式,事件,信号量。 目录 一:理论 二:WaitHandle 三:AutoResetEvent 四:ManualResetEvent 五:总结 一:理论 我们晓得线程同步可分为,用户模式构造和内核模式构造。 ...

    操作系统课程设计生产者和消费者问题源代码

    第三个字段表示在进入相应线程后,在进行生产和消费动作前的休眠时间,以秒计时;这样做的目的是可以通过调整这一列参数,控制开始进行生产和消费动作的时间。如果是代表生产者,则该行只有三个字段。如果代表消费者...

    操作系统课程设计(生产者-消费者,存储管理,虚拟存储器

    2、用信号量机制解决进程(线程)的同步与互斥问题。 二、实验目的 1.掌握基本的同步互斥算法,理解生产者和消费者模型。 2.了解Windows 2000/XP中多线程的并发执行机制,线程间的同步和互斥。 3.学习使用Windows ...

    嵌入式linux系统开发课程设计

    员+1,要求利用互斥锁,使每次输出a[0]==a[99]。 4)创建两线程,A线程每2秒打印一次字母A,B线程每秒打印一次 字母B,要求利用同步信号量,使输出字母B总是在A之后。 任务4、Linux应用程序开发实践,任选以下任务之一...

    操作系统实验报告-.docx

    7 一、实验目的和要求 通过本实验掌握在Linux操作系统中遵循Posix线程标准接口进行多线程程序编程,熟练掌握线程的创建pthread_create(),线程的终止pthread_exit(),等待线程合并pthread_join()等线程控制操作,...

Global site tag (gtag.js) - Google Analytics