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

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

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


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


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()方法声明为临界区。每个线程在运行前,都要先查看账户对象的锁定状态。如果对象被锁定,则等待;如果对象未锁定,则获得使用权,运行临界区中的代码,即使线程执行被打断,也不释放对象锁,只有当线程执行完才释放对象锁。这样不仅使得任何时刻只有一个线程对同一个账户进行操作,而且保证每个线程执行的多个操作是连续的,期间不会被其他线程干扰,最终查看金额、存入金额和剩余金额的结果相符,保证了数据的完整性和一致性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值