1.案例分析-01
通过代码实现火车票出售的例子
在实现代码之前先对问题进行分析:火车票出售应该是在多个窗口进行的(即多个线程),以一个车的班次来说,该班次的火车票张数即为多个窗口共享的数据
即这份共享数据为出售特定班次的火车票,这个动作在多个窗口都是不变的,变更的只有火车票的剩余张数.代码实现如下:
package org.lkl.thead;/** * * Function : 多线程共享数据 * * @author : Liaokailin CreateDate : 2014-6-13 version : 1.0 */public class MultiThreadShareData { public static void main(String[] args) { SellTicket s = new SellTicket(); // 共享对象 new Thread(s, "1").start(); new Thread(s, "2").start(); new Thread(s, "3").start(); new Thread(s, "4").start(); new Thread(s, "5").start(); }}/** * 出售火车票 */class SellTicket implements Runnable { private int ticket = 100; @Override public void run() { while (true) { synchronized (SellTicket.class) { if (ticket > 0) { try { Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } ticket-- ; System.out.println("窗口" + Thread.currentThread().getName() + "出售合肥到北京火车票剩余车票" + ticket); }else{ break ; } } } }}
通过上面的代码可以发现,多线程共享的数据是卖火车票,而这个动作在各个线程中都是不变的,因此可以做如下的小结:
多线程中各个线程执行的代码相同,那么可以使用同一个Runnable对象来实现共享数据。
2. 案例分析-02
和上面问题乡对应的是 如果多个线程中执行的代码不同呢? 该如何去处理数据之间的共享?
例如:
设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1 。
通过问题可以发现,线程中执行的代码显示是不同的,一个是j++ 一个是j--,在前面我们说过 要同步互斥的代码或者算法应该分别放在独立的方法中,并且这些方法都放在同一个类中,这样子
比较容易实现他们之间的同步互斥和通信
那么可以设计如下的业务类:
class Business{ private int j = 0; public synchronized void increment(){ j++ ; System.out.println(Thread.currentThread().getName()+",j="+j); } public synchronized void decrement(){ j-- ; System.out.println(Thread.currentThread().getName()+",j="+j); }}
要实现多个线程的不同操作,可以将不同的操作防止在不同的Runnable中 然后将不同的Runnable对象传递给不同的线程
package org.lkl.thead;/** * 设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1 */public class ShareDataFoo { public static void main(String[] args) { Business b = new Business() ; for(int i= 0 ;i<2;i++){ new Thread(new OperatorFoo01(b)).start(); new Thread(new OperatorFoo02(b)).start(); } } }class OperatorFoo01 implements Runnable{ private Business business; public OperatorFoo01(Business business){ this.business = business ; } @Override public void run() { business.increment() ; }}class OperatorFoo02 implements Runnable{ private Business business; public OperatorFoo02(Business business){ this.business = business ; } @Override public void run() { business.decrement() ; }}class Business{ private int j = 0; public synchronized void increment(){ j++ ; System.out.println(Thread.currentThread().getName()+",j="+j); } public synchronized void decrement(){ j-- ; System.out.println(Thread.currentThread().getName()+",j="+j); }}
运行代码得到的结果:
Thread-0,j=1Thread-1,j=0Thread-2,j=1Thread-3,j=0
我们可以做个小结:
如果每个线程执行的代码不同,这时候需要不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享
1.将共享数据封在在一个对象中,然后将这个对象逐一传递给各个不同Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥
和通行 (上面我们的例子就是采用这样的方式)
2.将这些Runnable对象作为某个类的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作也分配给外部类,以便对共享数据进行的各个操作的互斥和通信,作为内部类的各个
Runnable对象调用外部类的这些方法
对于第二点同样以上面的例子得到如下代码:
package org.lkl.thead;/** * 设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1 */public class ShareDataFoo2 { public static void main(String[] args) { ShareDataFoo2 foo = new ShareDataFoo2() ; for(int i= 0 ;i<2;i++){ new Thread(foo.new OperAdd()).start(); new Thread(foo.new OperSub()).start(); } } private int j = 0 ; public synchronized void increment(){ j++ ; System.out.println(Thread.currentThread().getName()+",j="+j); } public synchronized void decrement(){ j-- ; System.out.println(Thread.currentThread().getName()+",j="+j); } class OperAdd implements Runnable{ @Override public void run() { increment() ; } } class OperSub implements Runnable{ @Override public void run() { decrement() ; } } }
也可以对上面的两种方法进行组合:
将共享数据封装到另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象本身上来完成,该对象作为这个外部类中的成员变量或者方法中的局部变量,每个线程的Runnable对象
作为外部类中的成员内部类或局部内部类,如下面代码:
package org.lkl.thead;/** * 设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1 */public class ShareDataFoo3 { public static void main(String[] args) { ShareDataFoo3 foo = new ShareDataFoo3() ; for(int i= 0 ;i<2;i++){ new Thread(foo.new OperatorFoo01()).start(); new Thread(foo.new OperatorFoo02()).start(); } } private Business2 business = new Business2() ; class OperatorFoo01 implements Runnable{ @Override public void run() { business.increment() ; } } class OperatorFoo02 implements Runnable{ @Override public void run() { business.decrement() ; } } }class Business2{ private int j = 0; public synchronized void increment(){ j++ ; System.out.println(Thread.currentThread().getName()+",j="+j); } public synchronized void decrement(){ j-- ; System.out.println(Thread.currentThread().getName()+",j="+j); }}