java并发编程之:线程共享数据的方式
多个线程对共同数据的访问的实现,要根据情况而定
(1)当访问共同的代码的时候:可以使用同一个Runnable对象,这个Runnable对象中有这个共享数据,比如卖*系统就可以这么做。或者这个共享数据封装在一个对象当中,然后对这个对象加锁,也可以实现数据安全访问。
(2)当各个线程访问的代码不同时:这时候要使用不同的Runnable对象,有两种实现方式:
a)将共享数据封装在另一个对象当中,然后将这个对象逐一的转递给各个Runnable对象。操作数据的方法也放进这个对象当中。这样容易实现各个线程对这个数据的同步和共享。
b)将Runnable对象作为某一个类中的内部类,共享数据作为这个外部类的成员变量,每个线程对共享数据的访问也交给外部类的方法,比便对共享数据的互斥和通信。Runnable对象调用外部类的操作这个共享数据的方法。
还有一种方式是a)和b)的结合实现,把共享数据封装到一个对象当中去,这个对象也实现对这个共享数据的操作,这个对象作为外部类的成员变量。然后再创建多个Runnable对象做为内部类,操作这个对象。
一、每个线程执行的代码相同,可以使用同一个Runnable对象
public class SellTicket { public static void main(String[] args) { Ticket t = new Ticket(); new Thread(t).start(); new Thread(t).start(); }}class Ticket implements Runnable{ private int ticket = 10; @Override public void run() { while(ticket>0){ synchronized (this){ if(ticket > 0){ ticket--; System.out.println("当前*数为:"+ticket); } } } }}
二 1)当各个线程访问的代码不同时:这时候要使用不同的Runnable对象,将共享数据封装在另一个对象当中,然后将这个对象逐一的转递给各个Runnable对象。操作数据的方法也放进这个对象当中。这样容易实现各个线程对这个数据的同步和共享
public class MultiThreadShareData { public static void main(String[] args) { ShareData1 data2 = new ShareData1(); new Thread(new MyRunnable1(data2)).start(); new Thread(new MyRunnable2(data2)).start(); }} class MyRunnable1 implements Runnable{ private ShareData1 data1; public MyRunnable1(ShareData1 data1){ this.data1 = data1; } public void run() { data1.decrement(); } } class MyRunnable2 implements Runnable{ private ShareData1 data1; public MyRunnable2(ShareData1 data1){ this.data1 = data1; } public void run() { data1.increment(); } } class ShareData1 { private int j = 0; public synchronized void increment(){ j++; } public synchronized void decrement(){ j--; } }
二 2)把共享数据封装到一个对象当中去,这个对象也实现对这个共享数据的操作,这个对象作为外部类的成员变量。然后再创建多个Runnable对象做为内部类,操作这个对象
public class MultiThreadShareData { public static void main(String[] args) { final ShareData1 data1 = new ShareData1();//现在为局部变量,也可以写成类变量static类型(在main方法外声明) new Thread(new Runnable(){ @Override public void run() { data1.decrement(); } }).start(); new Thread(new Runnable(){ @Override public void run() { data1.increment(); } }).start(); }} class MyRunnable1 implements Runnable{ private ShareData1 data1; public MyRunnable1(ShareData1 data1){ this.data1 = data1; } public void run() { data1.decrement(); } } class MyRunnable2 implements Runnable{ private ShareData1 data1; public MyRunnable2(ShareData1 data1){ this.data1 = data1; } public void run() { data1.increment(); } } class ShareData1 { private int j = 0; public synchronized void increment(){ j++; } public synchronized void decrement(){ j--; } }
总结:对于要是下同步互斥的代码要放在不同的方法中,并且放在同一个对象当中,容易实现互斥和通信,并且也有利于日后的维护。这样思路也很清晰。
多线程间进行通讯例子:(wait(),notify())
public class TraditionalThreadCommunication { /** * @param args */ public static void main(String[] args) { final Business business = new Business(); new Thread( new Runnable() { @Override public void run() { for(int i=1;i<=20;i++){ business.sub(i); } } } ).start(); for(int i=1;i<=20;i++){ business.main(i); } }} class Business { private boolean bShouldSub = true; public synchronized void sub(int i){ while(!bShouldSub){//建议用while 因为线程有时候会被假唤醒,线程没有被通知的时候被唤醒 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for(int j=1;j<=10;j++){ System.out.println("sub thread sequence of " + j + ",loop of " + i); } bShouldSub = false; this.notify(); } public synchronized void main(int i){ while(bShouldSub){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for(int j=1;j<=100;j++){ System.out.println("main thread sequence of " + j + ",loop of " + i); } bShouldSub = true; this.notify(); } }
锁是上在代表要操作的资源的类内部方法中的,而不是线程代码中的。
synchronized 锁什么对象,wait 和 notify 就作用在什么对象上。否则会报错。