Java多线程-45-多线程安全问题--火车票卖票

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011541946/article/details/82990398

这篇通过火车票卖票来逐步演示多线程的安全问题,加入一共卖100张火车票,分成4个窗口卖。也就是,四个窗口就是4个线程,如何保证4个窗口卖票都正确。

1.先实现4个窗口卖票的代码

package thread;

public class Demo3_Ticket {

	public static void main(String[] args) {
		//创建四个线程
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();

	}

}

class Ticket extends Thread {
	
	private int tickets = 100;
	public void run () {
		while(true){
			if(tickets == 0){
				break;
			}
			System.out.println(getName() + "...这是第" + tickets-- + "号票");
		}
	}
	
}

上面的条件是,只要第几号票不等于0,就做tickets--操作。运行一次,发现实际上四个线程卖了400张票。因为new了四个ticket对象,每一个ticket对象都有自己的tickets=100这个成员变量。所以,变成了各自卖自己100张火车票的情况。

2.解决各自卖各自火车票的问题

上面每个对象都有自己100这个成员变量,我们需要把这个变量设置成共享,这样四个线程就能共享100张,所以在tickets设置static,看看效果。

private static int tickets = 100;

运行结果是没有问题。但是我们在if语句和打印语句之间可能还需要执行其他代码,如果添加执行其他代码,卖票的结果就有问题。代码修改如下。

package thread;

public class Demo3_Ticket {

	public static void main(String[] args) {
		//创建四个线程
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();

	}

}

class Ticket extends Thread {
	
	private static int tickets = 100;
	public void run () {
		while(true){
			if(tickets == 0){
				break;
			}
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(getName() + "...这是第" + tickets-- + "号票");
		}
	}
	
}

运行一下,神奇的发现,会一直运行下去,票可以卖到负数。如果不手动停止,会一直卖负数。有人说我们把if条件 tickets == 0改成<=0不就不会卖负数票了嘛。真的吗。

Thread-2...这是第2号票
Thread-3...这是第1号票
Thread-1...这是第0号票
Thread-0...这是第-1号票
Thread-2...这是第-2号票

为什么还是有负数呢?上面我们添加了sleep代码,有可能有一个线程或者4个线程都进行了sleep, 例如线程1开始进行sleep,接着线程2,线程3,线程4都依次进入sleep,某一个时候,线程1起来干活了,发现ticket=0,不符合<=0的条件就会跳出循环。执行打印语句,tickets--的结果就为-1,线程2跟着后面,也跳出循环,由于前面等于-1,所以线程2打印ticket--的结果就是-2. 如何解决这个问题,原因就是tickets这个变量,在循环判断和tickets--都用到,但是这段代码没有进行同步。

3.解决同步tickets问题

在titck类的方法中添加synchronized同步。

package thread;

public class Demo3_Ticket {

	public static void main(String[] args) {
		//创建四个线程
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();

	}

}

class Ticket extends Thread {
	
	private static int tickets = 100;
	public void run () {
		while(true){
			synchronized(this) {
				if(tickets <= 0){
					break;
				}
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(getName() + "...这是第" + tickets-- + "号票");
			}
		}
	}
	
}

运行之后,还是有负数票。原因是这里不能添加this为锁对象,因为new了四个对象,this代表不同new ticket对象,这个时候需要把this 改成Ticket.class就行。出了字节码对象,如果要添加一个成员对象作为锁对象,那么这个成员对象一定要用static修饰。

package thread;

public class Demo3_Ticket {

	public static void main(String[] args) {
		//创建四个线程
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();

	}

}

class Ticket extends Thread {
	
	private static int tickets = 100;
	private static Object obj = new Object();
	public void run () {
		while(true){
			synchronized(obj) {
				if(tickets <= 0){
					break;
				}
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(getName() + "...这是第" + tickets-- + "号票");
			}
		}
	}
	
}

建议还是写对字节码对象做锁对象就好。

猜你喜欢

转载自blog.csdn.net/u011541946/article/details/82990398
今日推荐