Java多线程并发处理(多窗口卖同一种票)

多线程

  多线程在生活中很常见,书上的案例大都是窗口售票。如果是三个窗口售卖不同的票,基本是没什么问题,大家各自卖就好了。

但是卖同一种票,就会涉及到对资源的读取、修改。并发为我们带来了很多好处,同时也有不少麻烦。

实例:

package ticketSell;
/**
 * 多线程售票,3个窗口售出2000张票
 * @author XMY
 *
 */

public class TicketWindow implements Runnable{
	
	private int num=20;

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			
			if(num>0) {
				System.out.println(Thread.currentThread().getName()+" 正在售出第  "+num+" 张票");//得到当前线程的名字
				num--;
			}else {
				System.out.println(Thread.currentThread().getName()+" 已售空");
				break;
			}
		
		
		}
	
	}
}

java提供了两个方法去编写线程类。一种是继承Thread,一种是实现接口Runnable。直接说的话大部分都会使用Runnable,因为java不支持多继承,所以很常见的一种情况就是如果你使用的是Thread那么这个类就不能继承别的类了。但接口可以很多。

首先是三个窗口售卖三种不同的票

public class TicketSell {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		TicketWindow tw1=new TicketWindow();
		TicketWindow tw2=new TicketWindow();
		TicketWindow tw3=new TicketWindow();
		
		Thread t1=new Thread(tw1);
		Thread t2=new Thread(tw2);
		Thread t3=new Thread(tw3);
		
		t1.start();
		t2.start();
		t3.start();
	}

}

因为是不同的票,所以“资源”也不一样,那么就要实例化三个线程类。

所以当“资源”一样时。我们要做的是把这一种“资源”放进三个“窗口”——实例化一个线程类,创建三个线程。

public class TicketSell {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		TicketWindow tickets=new TicketWindow();
		
		Thread t1=new Thread(tickets);
		Thread t2=new Thread(tickets);
		Thread t3=new Thread(tickets);
		
		t1.start();
		t2.start();
		t3.start();
	}

}

这时候看输出结果,会发现有很大的问题

出现了抢票、漏票、重票。基本这功能是完全没实现。但是看看自己的代码,好像也没什么问题。其实引起问题的就是,多线程的并发。

问题的发现和解决办法

  引起并发问题的是图一中的这两句:

比如说线程1在执行完println这条命令,正打算执行第二条语句,线程2刚好进来了,这时候num还没有被线程1修改,线程2得到的num是之前的至。这就导致了两个窗口卖了同一张票。情况更糟一点三个窗口都可能卖一张票。

教材里的解决办法很直接,因为引发问题是因为这里有两个语句对不对,那我就直接把这两句改成一句,输出的同时直接减减。直接避免线程并发的可能性。因为只有一句话而已你线程再怎么复杂也插不进来吧。

public class TicketWindow implements Runnable{
	
	private int num=20;

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			
			if(num>0) {
				System.out.println(Thread.currentThread().getName()+" 正在售出第  "+num--+" 张票");
		
			}else {
				System.out.println(Thread.currentThread().getName()+" 已售空");
				break;
			}	
		
		}
	
	}
}

这时候再看输出结果也没什么问题(顺序不同是因为线程并行运行抢占资源引起的)

多线程同步

  问题看似解决了。但这个方法其实还是不可行。首先线程的抢占还是在发生,最关键的是总不能以后用线程的时候都浓缩成一句话,这是不现实的。其实发生问题的原因就是数据在访问、修改的时候,没能来的及同步。只要做好了数据同步,那么再多线程也不会出现问题。

  为了实现同步,java提供了对象锁的方法。使得其他线程进入阻塞状态,强行的让其他线程等待,等第一个线程访问、修改好数据后,其他的线程才能进来。使用关键字synchronized

public class TicketWindow implements Runnable{
	
	private int num=20;

	@Override
	public  void run() {
		// TODO Auto-generated method stub
		while(true) {
			//同步代码块,this代表这个对象的对象锁
			synchronized(this) {
			if(num>0) {
				
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+" 正在售出第  "+num--+" 张票");//得到当前线程的名字 
				
			}else {
				//System.out.println(Thread.currentThread().getName()+" 已售空");
				break;
			}
			}

		}
	
	}
}

同时也可以对整个方法使用

public class TicketWindow implements Runnable{
	
	private int num=20;

	@Override
	public synchronized void run() {
		// TODO Auto-generated method stub
		while(true) {	
			if(num>0) {
				
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+" 正在售出第  "+num--+" 张票");//得到当前线程的名字 
				
			}else {
				//System.out.println(Thread.currentThread().getName()+" 已售空");
				break;
			}	
		}
	
	}
}

猜你喜欢

转载自blog.csdn.net/xieminyao123/article/details/81953776
今日推荐