多线程之卖票程序

来源:互联网 发布:网络安全法全文 编辑:程序博客网 时间:2024/05/17 02:14

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

程序1:

class MyThread implements Runnable{/*一百张票*/private int tickets = 100;@Overridepublic 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;@Overridepublic 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;@Overridepublic 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;@Overridepublic 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;@Overridepublic 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");@Overridepublic 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();}}

这种方式也是正确的。


0 0
原创粉丝点击