Java多线程学习2——互斥
一、前言
在上一节 (http://www.cnblogs.com/lzhen/p/3917966.html) 中,通过实现Runnable接口,可以实现多线程中的资源的共享,解决了一些基本的问题,但是在实际使用过程中,直接使用其中的第四节中的方法却会产生一些不可预知的问题,现在我们对其中的代码稍作修改,如下所示:
class MyThread implements Runnable { private int ticket = 5; //5张票 public void run() { for (int i=0; i<=5; i++) { if (this.ticket > 0) { System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } public class TestThread { public static void main(String [] args) { MyThread my = new MyThread(); new Thread(my, "1号窗口").start(); new Thread(my, "2号窗口").start(); new Thread(my, "3号窗口").start(); } }
这段代码运行的结果为:
1号窗口正在卖票5 3号窗口正在卖票3 2号窗口正在卖票4 2号窗口正在卖票2 1号窗口正在卖票1 3号窗口正在卖票2
当然这个结果也就有很大的不确定性,出现这样的问题的原因是不同的线程在共享同样的资源的时候,出现了碰撞,有可能线程1改变了共享的数据,还没来得及输出,线程2已经使用了,这样的问题在实际中是不允许的。而互斥就是解决这种临界资源问题的一种最简单的方法。
二、synchronized关键字
synchronized关键字是一个修饰符,可以修饰代码块和方法。它的作用是,对于同一个对象来说,当不同的线程都来调用同一个方法或者代码块的时候,必须等待前一个线程执行完之后,才能够开始执行这个方法或者代码块。,使用synchronized关键字修改上面代码,如下所示:
import java.awt.Desktop.Action; class MyThread implements Runnable { private int ticket = 5; // 5张票 public void run() { for (int i = 1; i <= 5; i++) { synchronized (this) { if (this.ticket > 0) { action(this.ticket); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.ticket--; } } } } public synchronized void action(int ticket) { System.out.println(Thread.currentThread().getName() + "正在卖票" + ticket); } } public class TestThread { public static void main(String[] args) { MyThread my = new MyThread(); new Thread(my, "1号窗口").start(); new Thread(my, "2号窗口").start(); new Thread(my, "3号窗口").start(); } }
这里为了演示synchronized的用法,在代码中不仅用synchronized修饰方法,还用来修饰了代码块,上述代码的实现效果为:
1号窗口正在卖票5 3号窗口正在卖票4 2号窗口正在卖票3 3号窗口正在卖票2 1号窗口正在卖票1
和预期的效果是一致的。