现在的位置: 首页 > 综合 > 正文

多线程之卖票程序

2018年06月05日 ⁄ 综合 ⁄ 共 3377字 ⁄ 字号 评论关闭

总共有100张票,两个站点同时出售。

程序1:

class MyThread implements Runnable{

	/*一百张票*/
	private int tickets = 100;
	
	@Override
	public void run() {
		while (true){
			if (tickets > 0){
				System.out.printf("%s线程正在卖出第%d张票!\n",Thread.currentThread().getName(),tickets);
				
				tickets --;
			} else {
				break;
			}
		}
		
	}
	
}
public class ThreadSynTest {

	
	public static void main(String[] args) {
		
		/*第一个站点卖票*/
		MyThread t1 = new MyThread();
		new Thread(t1).start();
		
		/*第二个站点卖票*/
		MyThread t2 = new MyThread();
		new Thread(t2).start();

	}

}

显示结果:

从结果可以发现,这两个线程卖的是各自的100张票,显然是不对的。

程序二:我们把票数的变量改为静态的。

/**
 * 把票数的变量改为静态的
 * @author Liao
 *
 */
class MyThread implements Runnable{

	/*一百张票*/
	private static int tickets = 100;
	
	@Override
	public void run() {
		while (true){
			if (tickets > 0){
				System.out.printf("%s线程正在卖出第%d张票!\n",Thread.currentThread().getName(),tickets);
				
				tickets --;
			} else {
				break;
			}
		}
		
	}
	
}
public class ThreadSynTest {

	
	public static void main(String[] args) {
		
		/*第一个站点卖票*/
		MyThread t1 = new MyThread();
		new Thread(t1).start();
		
		/*第二个站点卖票*/
		MyThread t2 = new MyThread();
		new Thread(t2).start();

	}

}

显示结果:

从图中可以看出,还是有问题!

产生上述结果的原因是程序在执行过程中线程是来回自由切换的,有可能第一个线程刚卖出票,数量还没来得及减一,就跳到了线程二。

从上面的分析来看,要想使得多个线程操作一个资源,我们必须使用一个互斥的手段来限制多个线程同时操作一个资源。

Java提供了Synchronized关键字来确保线程的同步。

synchronized可以用来修饰一个方法和一个方法内部的某个代码块。

1:修饰代码块:

synchronized(a){
	//同步代码块
}

上述代码表示当前线程霸占a对象,并执行代码块中的内容,其他线程无法执行代码块中的内容,只有当前线程执行完成之后,释放了对a对象的霸占,其他线程才有可能执行代码块中的内容。

总而言之:synchronized的功能就是一个线程正在操作某资源的时候,将不允许其他线程操作该资源,即一次只允许一个线程处理该资源。

2:修饰方法:

synchronized修饰一个方法时,实际霸占的是该方法的this指针所指向的对象,就是正在调用该方法的对象

我们先来看几种有问题的程序:

class MyThread1 implements Runnable {

	/* 一百张票 */
	private static int tickets = 100;

	@Override
	public void run() {

		synchronized (this) {
			
			while (true) {
				/* 不能放在外面:放在外面的话就表示一个线程跳进来之后,就互斥了,其他线程就跳不进来,导致只有一个线程在卖票 */

				if (tickets > 0) {
					System.out.printf("%s线程正在卖出第%d张票!\n", Thread.currentThread().getName(), tickets);

					tickets--;
				} else {
					break;
				}
			}
		}

	}

}

public class ThreadSynTest3 {

	public static void main(String[] args) {

		MyThread1 t = new MyThread1();

		/* 第一个站点卖票 */
		Thread thread1 = new Thread(t);
		thread1.start();

		/* 第二个站点卖票 */
		Thread thread2 = new Thread(t);
		thread2.start();

	}

}

程序输出的结果:

结果显示,永远都是一个线程在卖票;我觉得导致这个问题的原因是线程的启动也是调用run()方法,程序中run()方法的第一句代码就是执行synchronized这就导致了一开始就锁定了某个线程。直到这个线程把票都卖完了。

示例代码二:

class A implements Runnable{

	private int tickets = 100;
	
	
	@Override
	public synchronized void run() {
	
		while (true){
			if (tickets > 0){
				System.out.printf("%s线程正在卖出第%d张票!\n",Thread.currentThread().getName(),tickets);
				
				tickets --;
			} else {
				break;
			}
		}
		
	}
	
}

public class ThreadTest2 {

	
	public static void main(String[] args) {
		
		A a = new A();
		
		new Thread(a).start();
		new Thread(a).start();
	}

}

这个程序的问题和上一个程序是类似的,程序一执行,就锁定了某个线程导致了这个线程把票全卖完了。

正确程序:

class MyThread implements Runnable {

	/* 一百张票,注意这里要把变量定义为静态的 */
	private static int tickets = 100;


	@Override
	public void run() {

		while (true) {
			/*不能放在外面:放在外面的话就表示一个线程跳进来之后,就互斥了,其他线程就跳不进来,导致只有一个线程在卖票*/
			synchronized (this) {
				
				if (tickets > 0) {
					System.out.printf("%s线程正在卖出第%d张票!\n", Thread.currentThread().getName(), tickets);

					tickets--;
				} else {
					break;
				}
			}
		}

	}

}

public class ThreadSynTest {

	public static void main(String[] args) {

		MyThread t = new MyThread();

		/* 第一个站点卖票 */
		Thread thread1 = new Thread(t);
		thread1.start();

		/* 第二个站点卖票 */
		Thread thread2 = new Thread(t);
		thread2.start();

	}

}

程序结果:

第二种方式(正确):

/**
 * 通过继承的方式创建线程
 * @author Liao
 *
 */
class SyncTread extends Thread {

	/* 100张票 */
	private static int tickets = 100;
	/* 控制同步的标示 */
	private static String str = new String("s");

	@Override
	public void run() {

		while (true) {
			
			synchronized (str) {
				
				if (tickets > 0) {
					System.out.printf("%s线程正在卖出第%d张票\n", Thread.currentThread().getName(), tickets);
					tickets --;
				} else {
					break;
				}
			}
		}
	}
}

public class ThreadSyncTest2 {

	public static void main(String[] args) {

		/*第一个线程卖票*/
		SyncTread t1 = new SyncTread();
		t1.start();
		
		/*第一个线程卖票*/
		SyncTread t2 = new SyncTread();
		t2.start();
	}

}

这种方式也是正确的。

抱歉!评论已关闭.