一.如果每个线程执行的代码相同,可以使用同一个Runnable接口子类对象,这个Runnable接口子类对象中有那个共享数据,例如,买票系统就可以这么做。
二.如果每个线程执行的代码不同,这时候需要用不同的Runnable接口子类对象,有如下两种方式来实现这些Runnable接口子类对象之间的数据共享:
1.将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable接口子类对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
2.将这些Runnable接口子类对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable接口子类对象调用外部类的这些方法。
3.上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable接口子类对象作为外部类中的成员内部类或局部内部类。
总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。
//使用方式一
class Ticket implementsRunnable{private int ticket=100;
@Overridepublic synchronized voidrun() {//TODO 自动生成的方法存根
while(true)if(ticket>0)
System.out.println(Thread.currentThread().getName()+"..."+ticket—);
}
}classTicketTest{public static voidmain(String[] args){
Ticket t=newTicket();newThread(t).start();newThread(t).start();
}
}
//方式二:1
classResource{private int j=0;public synchronized voidadd(){
j=j+1;
System.out.println(Thread.currentThread().getName()+"..."+j);
}public synchronized voidreduce(){
j=j-1;
System.out.println(Thread.currentThread().getName()+"..."+j);
}
}class addImp implementsRunnable{privateResource r;public addImp(Resource r){//将Resource对象传递过来
this.r=r;
}
@Overridepublic voidrun() {//TODO 自动生成的方法存根
r.add();
}
}class reduceImp implementsRunnable{privateResource r;public reduceImp(Resource r){//将Resource对象传递过来
this.r=r;
}
@Overridepublic voidrun() {//TODO 自动生成的方法存根
r.reduce();
}
}classMainClass{public static voidmain(String[] args){
Resource r=new Resource();//用的依然是方式一定义的Resource类
for(int i=0;i<2;++i)new Thread(newaddImp(r)).start();for(int i=0;i<2;++i)new Thread(newreduceImp(r)).start();
}
}
//方式二:2
classMainClass2{private static int j=0;//共享数据作为这个外部类中的成员变量
public static synchronized void add(){//每个线程对共享数据的操作方法也分配给外部类
j=j+1;
System.out.println(Thread.currentThread().getName()+"..."+j);
}public static synchronized voidreduce(){
j=j-1;
System.out.println(Thread.currentThread().getName()+"..."+j);
}public static void main(String[] args){//这里静态只能访问静态,以上成员需要static修饰符,根据需要
for(int i=0;i<2;++i){new Thread(newRunnable(){
@Overridepublic voidrun() {
add();
}
}).start();new Thread(newRunnable(){
@Overridepublic voidrun() {
reduce();
}
}).start();
}
}
}
//方式2:3
classMultithreadShare7 {private static Resource r=newResource();public static voidmain(String[] args){//final Resource r=new Resource();//r作为局部变量,被内部类访问需要final修饰
for(int i=0;i<2;++i){new Thread(newRunnable(){
@Overridepublic voidrun() {
r.add();
}
}).start();new Thread(newRunnable(){
@Overridepublic voidrun() {
r.reduce();
}
}).start();
}
}
}
线程间通信例子:
packagecom.itheima.thread;/*子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程循环
100,如此循环50次,请写出程序*/
//使用的3思想
classRunCode{private boolean flag=false;//使用标记进一步控制
public synchronized voidsubThreadCode(){while(flag)try{this.wait();
}catch(InterruptedException e) {
e.printStackTrace();
}for(int i=0;i<5;++i)
System.out
.println(Thread.currentThread().getName()+ "..." +i);
flag=true;this.notify();
}public synchronized voidmainThreadCode(){while(!flag)try{this.wait();
}catch(InterruptedException e) {//TODO 自动生成的 catch 块
e.printStackTrace();
}for(int i=0;i<3;++i)
System.out
.println(Thread.currentThread().getName()+ "..." +i);
flag=false;this.notify();
}
}public classThreadInterviewQuestion4 {/***@paramargs*/
public static voidmain(String[] args) {final RunCode rc=newRunCode();new Thread(newRunnable(){
@Overridepublic voidrun() {for(int i=0;i<10000;++i){//没有按照原题目,为了看是否有”奇迹”发生 rc.subThreadCode();
}
}
}).start();for(int i=0;i<10000;++i){
rc.mainThreadCode();
}
}
}/*规范代码:
1.多个线程执行代码放在共享资源中(这样做便于管理,扩展)
2.等待唤醒机制一般都要使用标记
3.尝试各种方法,发现最好使用if判断是否wait,然后线程执行代码,置换标记,唤醒(其它方式各种"奇迹") 4.即使两个线程进行通信,依然建议while判断标记,api中一句话:对于某一个参数的版本,实现中断和虚假唤醒是可能的,而且此方法应始终在循环中使用更加安全*/