`

线程间的竞争关系与线程互斥

 
阅读更多
线程间的竞争关系与线程互斥

1.线程间的竞争关系
        同一个进程中的多个线程由系统调度而并发执行时,彼此之间没有直接联系,并不知道其他线程的存在,一般情况下,也不受其他线程执行的影响。但是,如果两个线程要访问同一资源,则线程间存在资源竞争关系,这是线程间的间接制约关系。一个线程通过操作系统分配得到该资源,另一个将不得不等待,这时,一个线程的执行可能影响到同其竞争资源的其他线程。
       在极端的情况下,被阻塞线程永远得不到访问权,从而不能成功地终止。所以,资源竞争出现了两个问题 :
        一是死锁(deadlock)问题,一组线程如果都获得了部分资源,还想要得到其他线程所占用的资源,最终所有的线程将陷入死锁;
        二是饥饿(starvation)问题,一个线程由于其他线程总是优于它而被无限期拖延。
例如:一个路口的交通信号灯控制,如果4个方向都是绿灯,汽车抢行造成路堵,这是死锁。
      如果4个方向都是红灯,谁也不能走,这是饥饿。
操作系统负责资源分配,操作系统必须协调好线程对资源的争用。操作系统需要保证诸线程能互斥地访问临界资源,既要解决饥饿问题,也要解决死锁问题
2.线程互斥和临界区管理
         线程互斥(mutual exclusion)是解决线程间竞争关系的手段。线程互斥是指若干个线程要使用同一共享资源时,任何时刻最多允许一个线程去使用,其他要使用该资源的线程必须等待,直到占有资源的线程释放该资源。
         把共享变量代表的资源称为临界资源(critical resource),并发线程中与共享变量有关的程序段称为临界区(critical section)。由于与同一变量有关的临界区分散在各有关线程的程序段中,而各线程的运行速度不可预知,操作系统对共享一个变量的若干线程进入各自临界区有以下3个调度原则:
      (1)一次至多一个线程能够在它的临界区内。
      (2)不能让一个线程无限期地留在它的临界区内。
      (3)不能强迫一个线程无限地等待进入它的临界区。特别地,进入临界区的任一线程不能妨碍正等待进入的其他线程的进展。
      把临界区的调度原则总结成四句话:无空等待、有空让进、择一而入、算法可行。算法可行,是指不能因为所选的调度策略造成线程饥饿甚至死锁。这样能保证一个线程在临界区执行时,不让另一个线程进入相关的临界区,即各线程对共享变量的访问是互斥的,就不会造成与时间有关的错误。
       操作系统提供“互斥锁”机制实现并发线程互斥地进入临界区,对共享资源进行操作。至于操作系统采用什么样的锁(信号灯、只读锁等)以及如何实现加锁和解锁等问题,Java程序员并不需要关心,这些细节都由操作系统和Java虚拟机来处理,程序员只需要在程序中声明哪个程序段是临界区即可,采用Java抽象的锁模型,就能够使程序在所有平台上可靠地、可预见地运行。

Java的线程互斥实现
   Java提供关键字synchronized用于声明一段程序为临界区,使线程对临界资源采用互斥使用方式。synchronized有两种用法:声明一条语句、声明一个方法。
(1)同步语句
   使用synchronized声明一条语句为临界区,该语句称为同步语句,语法格式如下:
         synchronized(对象)
                语句
    其中,<对象>是多个线程共同操作的公共变量,即需要被锁定的临界资源,它将被互斥地使用;<语句>是临界区,它描述线程对临界资源的操作,如果是多条语句需要用{}括起来成为一条复合语句。
      一个同步语句允许一个对象锁保护一个单独的语句(也包括一个复合语句),在执行这个语句前,必须获得这个对象锁。
     同步语句执行过程如下:
    当第1个线程进入临界区执行(语句)时,它获得临界资源即指定(对象)的使用权,并将对象加锁,然后执行语句对对象进行操作。
      在此过程中,如果有第2个线程也希望对同一个对象执行这条语句,由于作为临界资源的对象已被锁定,则第2个线程必须等候。
      当第1个线程执行完临界区语句,它将释放对象锁。
      之后,第2个线程才能获得对象的使用权并运行。
      这样,对于同一个对象,在任何时刻都只能有一个线程执行临界区语句,对该对象进行操作,其他竞争使用该对象的线程必须等待,直到对象锁被释放。这就实现了多个并发执行的交互线程间对同一个临界资源的互斥使用。

(2)同步方法
              使用synchronized声明一个方法,该方法称为同步方法,语法格式如下:
                synchronized   方法声明
    这样,同步方法的方法体成为临界区,互斥使用(锁定)的是调用该方法的对象。该声明与以下声明效果相同:
      方法声明
      
synchronized(this)
       {
            方法体
        }

  
同步语句与同步方法的行为基本相似,只是同步语句的作用范围小,它只是锁住一条语句(或复合语句)而不是完整的方法,并指定所要获得锁的对象而不调用方法的对象。这样就增加了灵活性,并且缩小了对象锁的作用域。

package com.jbx.thread;

public class SaveLock extends Thread{    //带互斥锁的存款线程类

    private Account account;  //账户  
    private double value;     //存款金额  
      
    public SaveLock(Account al ,double value){  
        this.account = al;  
        this.value = value;  
    }  

	public void run() {
		 synchronized (this.account) { // 声明临界区 

			double howmatch = this.account.getBalance();// 查看账户余额
			
			try {
				sleep(1); // 花费实际,线程执行被打断
			} catch (InterruptedException e) {
			}
			this.account.put(this.value);
			System.out.println(this.account.getName() + "账户:现有" + howmatch
					+ ",存入" + this.value + ",余额" + this.account.getBalance());
		 } 
	}
	 public static void main(String[] args) {  
	        Account wang = new Account("wang");  
	        (new SaveLock(wang,1000)).start();     
	        (new SaveLock(wang,200)).start();     
	        (new FetchLock(wang,300)).start();    
	    }  
}  
class FetchLock extends Thread{  
    private Account account;  //   
    private double value;     //   
      
    public FetchLock(Account al ,double value){  
        this.account = al;  
        this.value = value;  
    }  
      
    public void run(){ 
    	 synchronized(this.account){  //声明临界区,锁定指定账户对象 
	        double howmatch = this.account.getBalance();//查看账户余额  
	       
	        try {  
	            sleep(1);                              //花费实际,线程执行被打断  
	        } catch (InterruptedException e) {}  
	        System.out.println(this.account.getName()+"账户:现有"+howmatch+",取走"+this.account.get(this.value)+",余额"+this.account.getBalance());  
	    	 }  
    }
   
}  

 class Account {        //账户类  
    private String name;     //储户姓名  
    private double balance;  //账户余额  
      
    public Account(String name) {  
        this.name = name;  
        this.balance = 0;  
    }  
    public String getName() {     //返回账户名  
        return name;  
    }  
  
    public double getBalance() {   //查看账户余额  
        return balance;  
    }  
      
    public void put(double value) {   //存款操作,参数为存入金额  
        if(value>0)  
        this.balance += value;        //存款操作使余额值增加//存款操作,参数为取款金额,返回实际取到金额  
    }  
      
    public double get(double value){  //取款操作,参数为取款金额,返回实际取到金额  
        if(value>0){  
            if(value<=this.balance)  
                this.balance -= value;  //取款操作使余额值减少  
            else{  
                value= this.balance;    //取走全部余额  
                this.balance = 0;   
            }  
            return value;              //返回实际取款额  
        }  
        return 0;  
    }  
}  

运行结果:
wang账户:现有0.0,存入1000.0,余额1000.0
wang账户:现有1000.0,取走300.0,余额700.0
wang账户:现有700.0,存入200.0,余额900.0



本例将存/取款线程体设置位针对同一个账户对象互斥使用的临界区,也可以将put()和get()方法声明为临界区。每个线程在运行前,都要先查看账户对象的锁定状态。如果对象被锁定,则等待;如果对象未锁定,则获得使用权,运行临界区中的代码,即使线程执行被打断,也不释放对象锁,只有当线程执行完才释放对象锁。这样不仅使得任何时刻只有一个线程对同一个账户进行操作,而且保证每个线程执行的多个操作是连续的,期间不会被其他线程干扰,最终查看金额、存入金额和剩余金额的结果相符,保证了数据的完整性和一致性。
分享到:
评论

相关推荐

    线程间互斥2

    为了保证数据的一致性和完整性,避免竞态条件(race condition)的发生,就需要采取线程互斥机制来控制对共享资源的访问。 线程互斥的基本思想是,当一个线程正在访问共享资源时,其他试图访问该资源的线程会被阻塞...

    易语言线程互斥对象解决

    线程互斥对象允许我们限制对共享资源的访问,防止多个线程同时访问同一资源,从而避免数据竞争和不一致状态。 1. **线程互斥对象(Mutex)**: 线程互斥对象是一种同步机制,当一个线程获得了Mutex的所有权后,...

    JAVA实现线程间同步与互斥生产者消费者问题

    本项目通过一个生产者消费者问题的实例,展示了如何在Java中实现线程间的同步与互斥。 生产者消费者问题是经典的并发问题之一,它涉及到两个类型的线程:生产者和消费者。生产者负责生成数据(产品),而消费者则...

    操作系统实验(三)线程的互斥

    本实验旨在让学生通过实践操作深入了解和掌握在Windows系统环境下线程的创建与撤销的方法,熟悉并使用Windows系统提供的线程互斥API来解决多线程访问共享资源时产生的竞态条件问题。 #### 实验准备知识 为了实现...

    C#多线程互斥实例 多线程获取同一变量

    在这个"多线程互斥实例 多线程获取同一变量"的示例中,我们将探讨如何在多个线程中安全地访问共享资源,避免数据不一致性和竞态条件。 首先,我们需要理解多线程中的一些核心概念: 1. **线程**:线程是操作系统...

    多线程代码 经典线程同步互斥问题 生产者消费者问题

    e: 使用关键段解决子线程互斥问题 f: 利用事件实现线程同步问题 g: 利用互斥量来解决线程同步互斥问题 h: problem1 生产者消费者问题 (1生产者 1消费者 1缓冲区) problem1 more 生产者消费者问题 (1生产者 2...

    创建线程,利用互斥实现线程共享变量通信

    掌握线程创建和终止,加深对线程和进程概念的理解,会用同步与互斥方法实现线程之间的通信。 三、内容和要求 软件界面上点“创建线程” 按钮,创建三个生产者线程(P1,P2,P3)和两个消费者线程(C1,C2),生产者...

    C# 多线程的同步与互斥(使用Mutex和Event)

    当多个线程同时运行时,可能会引发数据竞争和不一致性问题,因此理解和掌握线程的同步与互斥至关重要。在给定的标题和描述中,我们关注的是如何利用Mutex和AutoResetEvent这两个类来解决这些问题。 Mutex(互斥锁)...

    3.线程间同步和通信之互斥锁(静态)

    在多线程编程中,线程间的同步和通信是至关重要的,这有助于保证程序的正确性和数据的一致性。在本教程中,我们将探讨“互斥锁”这一机制,它是实现线程同步的一种常见方法。互斥锁在STM32微控制器上使用RT-thread...

    多线程的小例子,线程互斥的问题

    本文将深入探讨标题和描述中提到的“多线程的小例子”以及线程互斥的概念。 线程互斥是多线程编程中的一个关键概念,用于确保在特定时间内,只有一个线程可以访问共享资源,以防止数据不一致性和竞态条件。在C#中,...

    linux上实现多进程和多线程实现同步互斥(源代码)

    线程间的同步互斥通常通过以下方法实现: 1. 互斥锁(Mutex):使用`pthread_mutex_init()`初始化互斥锁,`pthread_mutex_lock()`和`pthread_mutex_unlock()`进行锁定和解锁。当一个线程获得锁后,其他试图获取该锁...

    操作系统实验 多线程同步与互斥 java编写 有界面

    在“操作系统实验 多线程同步与互斥 java编写 有界面”的实验中,可能需要设计一个图形用户界面(GUI),通过按钮或事件触发线程的创建和同步操作,直观地展示线程间的交互和同步效果。例如,可以模拟银行账户转账,...

    C#.NET多线程实例6个(包括多线程基本使用,多线程互斥等全部多线程使用实例)

    本资源包含六个C#.NET多线程实例,涵盖了多线程的基本使用以及高级特性,如线程互斥,确保了线程安全和数据一致性。 1. **多线程基础**: - **创建线程**:在C#中,可以使用`Thread`类创建新线程。通过构造函数...

    第20章 Part3 多线程互斥与协作.pdf

    ### 第20章 Part3:多线程互斥与协作 #### 一、互斥(Mutual Exclusion) 互斥是指在线程编程中确保多个线程不会同时访问同一资源的技术。这种技术非常重要,因为如果不加以控制,多个线程对共享资源的并发访问...

    实例讲述线程的同步互斥

    **线程互斥** 是线程同步的一种特殊形式,确保同一时间只有一个线程能够访问特定的资源。互斥锁是实现线程互斥的常用工具。当一个线程获取了互斥锁后,其他试图获取该锁的线程会被阻塞,直到持有锁的线程释放它。在...

    解决多线程编程中的同步互斥问题

    // 进入各子线程互斥区域 g_nNum++; Sleep(0); printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_nNum); LeaveCriticalSection(&g_csThreadCode); return 0; } ``` 在这个示例中,有两个关键段:`g_...

    线程间实现同步互斥的方法

    当多个线程同时运行在同一个进程中时,为了确保数据的一致性和避免资源竞争,就需要实现线程间的同步和互斥。这篇文档将深入探讨如何在编程中实现这一目标。 标题:“线程间实现同步互斥的方法” 在多线程环境中,...

    C# 多线程互斥 两个线程交替工作

    在C#编程中,多线程是实现并发执行任务的关键技术。...总的来说,C#中的多线程互斥是通过各种同步机制来保证线程安全和顺序执行。合理使用这些机制,能有效地实现两个线程交替工作,从而提高程序的并发处理能力。

    线程进程互斥锁

    - **用途**:互斥锁常用于确保同一时间只有一个线程(或进程)访问特定的临界区,避免数据竞争(Race Condition),保证数据一致性。 3. **线程与进程互斥锁的应用**: - **文件操作**:在多个线程或进程中,如果...

    线程与互斥锁的应用

    在这个“线程与互斥锁的应用”场景中,我们有两个线程,一个名为echo,另一个名为cat,它们都涉及到读取手机IMEI(国际移动设备识别码)的操作。IMEI是手机设备的唯一标识,通常由15位数字组成,对于多线程应用来说...

Global site tag (gtag.js) - Google Analytics