JAVA学习->线程互斥与同步之车票售卖

一、什么叫线程的互斥

        日常生活中,总会有多个火车票售卖窗口,他们可以实现多个窗口对总的车票进行售卖,而不会发生在两个或多个窗口售卖同一张车票的情况,这个处理就叫做线程的互斥处理。

        例如,设有若干线程共享某个变量,而且都对变量有修改。如果它们之间不考虑相互协调工作,就会产生混乱。比如,线程A和B共用变量x,都对x执行增1操作。由于A和B没有协调,两线程对x的读取、修改和写入操作相互交叉,可能两个线程读取相同个x值,一个线程将修改后的x新值写入到x后,另一个线程也把自己对x修改后的新值写入到x。这样,x只记录后一个线程的修改作用。

          即,多个线程并行的修改数据,但同一时刻,对于同一对象,只能有一个线程对该对象进行访问及修改。


二、线程的互斥与同步

1、互斥及同步的概念

        间接相互制约可以称为互斥,直接相互制约可以称为同步,对于互斥可以这样理解,线程A和线程B互斥访问某个资源则它们之间就会产个顺序问题——要么线程A等待线程B操作完毕,要么线程B等待线程操作完毕,这其实就是线程的同步了。因此同步包括互斥,互斥其实是一种特殊的同步

        线程互斥:指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

          线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,目前实现线程同步的方法有很多,临界区对象就是其中一种。为多线程的同步,提供了wait()和notify()方法。wait()方法是,当一个线程执行了这个方法时,就会放弃互斥锁,进入互斥锁的等待队列。notify()方法是,唤醒互斥锁等待队列中的进线程,进入就绪状态。

2、互斥与同步的注意事项

       (1) wait()和notify()方法必须配对使用,执行wait()方法进入等待队列的线程,必须由另一线程执行notify()方法唤醒。

      (2)  wait()和notify()方法必须位于同步代码块中,也就是在synchronized代码块中。

       (3)  在某些情况下,可以使用notifyAll()方法代替notify()方法,唤醒等待队列中的所有线程。


三、互斥对象

        通常将多线程并发访问的资源称为临界资源。对临界资源的访问必须是互斥的,java可以为每一个对象设置一个“互斥锁”,保证同一时刻只有一个线程拥有互斥锁,其它线程必须等待拥有锁的线程释放后才可以获取。

        java中提供了synchronize关键字去实现“互斥锁”。当定义类、方法或者代码片段时,使用该关键字,就表示和该关键字相关联的对象有互斥锁。


四、车票售卖代码

package cn.itcast_08;

public class SellTicket {
	int ticketNum;		//总可售卖票数
	public SellTicket(int ticketNum) {
		this.ticketNum=ticketNum;
	}
	public synchronized void sellTicket(int num) {
		if(ticketNum-num>=0) {
			try {
				Thread.sleep(1000);
				ticketNum-=num;
				System.out.println("窗口:"+Thread.currentThread().getName()+"成功售卖"+num+"张票!");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else {
			System.out.println(",票量不够,票只剩下"+ticketNum+"张!");
		}
	}
}


package cn.itcast_08;

import java.util.Scanner;

public class SellThread extends Thread {
	SellTicket ticket;
	public SellThread(SellTicket ticket) {
		super();
		this.ticket=ticket;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in );
		while(ticket.ticketNum>0) {
			int num=sc.nextInt();	//键盘录入要购买的票数
			ticket.sellTicket(num);
		}	
	}
	
	public static void main(String[] args) throws InterruptedException {
		System.out.print("请输入可售卖的总票数:");
		Scanner sc=new Scanner(System.in );
		int allNum=sc.nextInt();	//键盘录入可卖的总票数
		SellTicket ticket=new SellTicket(allNum);
		
		SellThread t1=new SellThread(ticket);//线程1
		SellThread t2=new SellThread(ticket);//线程2
		
		t1.start();
		t2.start();
		
		t1.join();
		t2.join();
		
		System.out.println("目前剩余可售票数:"+ticket.ticketNum);
	}
}

运行结果如下:

请输入可售卖的总票数:50
20
窗口:Thread-1成功售卖20张票!

2
窗口:Thread-1成功售卖2张票!

4
窗口:Thread-1成功售卖4张票!

1
窗口:Thread-1成功售卖1张票!

3
窗口:Thread-0成功售卖3张票!
4
窗口:Thread-1成功售卖4张票!

2

窗口:Thread-0成功售卖2张票!

3

窗口:Thread-1成功售卖3张票!

4

窗口:Thread-0成功售卖4张票!
5

窗口:Thread-1成功售卖5张票!

6

,票量不够,票只剩下2张!
2
窗口:Thread-0成功售卖2张票!


注意:wait()方法会释放互斥锁,必须通过notify()方法唤醒互斥锁等待队列的线程,而sleep()方法不会。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值