Single Threaded Execution

它是“一个线程”的意思,就像一个桥,每次只能通过一个人。

下面测试:Gate是表示门的类,经过门(pass)会检查姓名与address的首字母是否相同,如果不同,输出
                  Passer继承了Thread,它一直反复通过门
                  Main中有三个Passer,让这三个Passer通过同一个门(这里意味着gate的信息是共享的)

public class Gate {
	private int counter=0;
	private String name="Nobody";
	private String address="Nowhere";
	
	public void pass(String name,String address){
		this.counter++;
		this.name=name;
		this.address=address;
		check();
	}
	private void check(){
		if(name.charAt(0)!=address.charAt(0))
			System.out.println("*****BROKEN*****"+toString());
	}
	
	@Override
	public String toString(){
		return "No."+counter+":"+name+","+address;
	}
}
public class Passer extends Thread{
	private final Gate gate;
	private final String myname;
	private final String myaddress;
	
	public Passer(Gate gate,String myname,String myaddress){
		this.gate=gate;
		this.myname=myname;
		this.myaddress=myaddress;
	}
	
	@Override
	public void run(){
		System.out.println(myname+" BEGIN");
		while(true){
			gate.pass(myname, myaddress);
		}
	}
}

 

public class Main {
	public static void main(String[] args){
		System.out.println("hit Ctrl+C to exit");
		Gate gate=new Gate();
		//三个Passer用了同一个gate
		new Passer(gate,"Alice","Alaska").start();
		new Passer(gate,"Bobby","Brazil").start();
		new Passer(gate,"Chris","Canada").start();
	}
}

 运行结果:

hit Ctrl+C to exit
Alice BEGIN
Bobby BEGIN
*****BROKEN*****No.878:Bobby,Brazil
*****BROKEN*****No.1387:Bobby,Brazil
*****BROKEN*****No.23170:Alice,Alaska
*****BROKEN*****No.28157:Bobby,Brazil
*****BROKEN*****No.54236:Alice,Alaska
*****BROKEN*****No.59996:Bobby,Brazil
*****BROKEN*****No.69240:Bobby,Brazil
*****BROKEN*****No.77988:Alice,Alaska
*****BROKEN*****No.83399:Bobby,Brazil
*****BROKEN*****No.91945:Alice,Alaska
*****BROKEN*****No.97449:Bobby,Brazil
*****BROKEN*****No.106786:Alice,Alaska
*****BROKEN*****No.140881:Bobby,Brazil
*****BROKEN*****No.149914:Alice,Alaska
*****BROKEN*****No.183557:Alice,Alaska
*****BROKEN*****No.188912:Bobby,Brazil
*****BROKEN*****No.198266:Bobby,Brazil
*****BROKEN*****No.206391:Alice,Alaska
*****BROKEN*****No.237655:Bobby,Brazil
*****BROKEN*****No.245603:Bobby,Brazil
*****BROKEN*****No.561550:Alice,Brazil
*****BROKEN*****No.561550:Alice,Brazil

。。。。。(这时第三个线程还没有运行,其实这三个线程的运行顺序是不可知的)

 

原因分析:这个线程在check时,另外一个线程改变了gate的属性值。

应该做如下修改来保证每个时刻只有一个Passer进入gate的pass方法:

public class Gate {
	private int counter=0;
	private String name="Nobody";
	private String address="Nowhere";
	
	public synchronized void pass(String name,String address){
		this.counter++;
		this.name=name;
		this.address=address;
		check();
	}
	private void check(){
		if(name.charAt(0)!=address.charAt(0))
			System.out.println("*****BROKEN*****"+toString());
	}
	
	@Override
	public synchronized String toString(){
		return "No."+counter+":"+name+","+address;
	}
}

 这里check()是不需要用synchronized,因为check是private的,且只有pass才会调用它,而pass已经设置为synchronized的了。而String是public的,如果它不设置为synchronized,那么如果有线程调用toString的同时,另外一个Passer线程正修改完name而准备修改address,那么这样造成显示不一致了。

 

        当多个线程共享同一个实例时候,多个线程独自改变实例的状态,使得实例丧失安全性。首先,需要找到不稳定的范围(临界区间),对临界区间进行保护,使同时执行的线程保持只有一条,这也就是Single Threaded Execution Pattern。当数据被多个线程访问、状态可能改变、需要确保安全时就可以使用这种模式。        java里可以用synchronized来实现临界区间。
synchronized void method(){....}

synchronized (obj){......}

void method(){
    lock();
    ....       //这里如果有return 或者异常,那么锁就没有解开
    unlock();
}

void method(){
    lock();
    try{
       ....    
    }
    finally{
        unlock();
    }
}
         这个模式会使程序的执行性能降低,因为在进入synchronized方法的时候要获取对象的锁定是需要时间的,当线程发生冲突的时候线程必须等待,这耗掉了很多时间。 
       从多线程的角度来讲,synchronized方法是具有原子性的,当它执行时是不允许其他线程进入这个方法的。在java规格中,char、int、对象引用类型等是具有原子性的,而long、double就不具有原子性了。如果一个线程执行longField=123L,而同时另外一个线程执行longField=456L,这样操作后,longField里面的值是无法保证的,甚至可能既不是123L也不是456L。在声明字段的时候加上volatile关键字,那么所有对该字段的操作就变得不可分割。因此,线程共享long或者double的字段的时候,必须在synchronized中操作或者声明加上volatile。
        这个模式使用时可能存在死锁的情况。例如Ally和Bob用同一套spoon和fork进餐时,当每个人各自那一样餐具时就出现了死锁。
package deadlock;

public class DeadLockTest {
	public static void main(String[] args){
		Tool spoon=new Tool("spoon");
		Tool fork=new Tool("fork");
		new EaterThread("Alice",spoon,fork).start();
		new EaterThread("Bob",fork,spoon).start();
	}
}
 
package deadlock;

public class Tool {
	private String toolname;
	public Tool(String name){
		toolname=name;
		}
	@Override
	public String toString(){
		return "["+toolname+"]";
	}
}
 
package deadlock;

public class EaterThread extends Thread{
	private String person;
	private final Tool leftTool;//左手拿的工具
	private final Tool rightTool;//右手拿的工具
	public  EaterThread(String person,Tool leftTool,Tool rightTool){
		this.person=person;
		this.leftTool=leftTool;
		this.rightTool=rightTool;
	}
    @Override
    public void run(){
    	while (true){
    		eat();
    	}
    }
	private void eat() {
		synchronized(leftTool){    //使用左手的工具,这个工具不许其他人使用
			System.out.println(person+" use"+leftTool+"L");
			synchronized(rightTool){//使用右手的工具,这个工具不许其他人使用
				System.out.println(person+" use"+rightTool+"R");
				System.out.println(person+" begin to eat!_1_2_3 finish eating!");
				System.out.println(person+" putdown "+rightTool);
			}
			System.out.println(person+" putdown "+leftTool);
			System.out.println("finish!");
		}
	}
}
 运行结果:
Alice use[spoon]L
Alice use[fork]R
Alice begin to eat!_1_2_3 finish eating!
Alice putdown [fork]
Alice putdown [spoon]
finish!
Alice use[spoon]L
Bob use[fork]L

这样一直出现等待。。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值