java多线程设计模式笔记之Single Thread Execution

现在有三个人要循环随机通过一个门,三个人的名字和国家首字母都一样,每当一个人通过门的时候,就检查这个人和国家是否一致,不一致则报错BROKEN.程序如下:

门:

import java.awt.Checkbox;


public class Gate {
	private int count = 0;
	private String name = "Nobody";
	private String country = "Nowhere";
	public void name() {
		
	} void pass(String name,String address) {
		// TODO Auto-generated method stub
		this.count++;
		this.name = name;
		this.country = address;
		Check();
	}
	
	
	
	public String toString() {
		return "No."+count+":"+name+","+country;
	}

	private void Check() {
		// TODO Auto-generated method stub
		if(name.charAt(0) != country.charAt(0)){
			System.out.println("BROKEN*************"+toString());
		}
	}
}
人(每个人穿越是独立的,故为一个线程)

public class UserThread extends Thread{
	private Gate gate;
	private String myname;
	private String mycountry;
	public UserThread(Gate gate, String name, String country) {
		this.gate = gate;
		this.myname = name;
		this.mycountry = country;
	}
	public void run() {
		// TODO Auto-generated method stub
		System.out.println(myname+"BEGIN");
		while (true) {
			gate.pass(myname, mycountry);
			
		}
	}
}
客户端程序:

public class Main {
	public static void main(String[] args){
		System.out.println("Testing Gate");
		Gate gate = new Gate();
		new UserThread(gate, "Bobe", "Brazil").start();
		new UserThread(gate, "Chen", "China").start();
		new UserThread(gate, "Alice", "America").start();

	}
}
每个人的名字首字母都和国家首字母一样,感觉应该不会出现BROKEN,但是看下结果:

Testing Gate
BobeBEGIN
AliceBEGIN
ChenBEGIN
BROKEN*************No.432:Alice,America
BROKEN*************No.543:Alice,Brazil
BROKEN*************No.811:Chen,America
BROKEN*************No.543:Alice,Brazil
BROKEN*************No.811:Chen,America
BROKEN*************No.1521:Alice,America
BROKEN*************No.1765:Alice,America
BROKEN*************No.2142:Alice,China
BROKEN*************No.1078:Alice,America
BROKEN*************No.2554:Bobe,Brazil
BROKEN*************No.2834:Bobe,Brazil
BROKEN*************No.3111:Alice,Brazil
BROKEN*************No.2142:Alice,China
BROKEN*************No.3495:Alice,America

(以下省略n条记录。。)

My god!出好多问题了!

为什么呢?因为所有线程共享一份数据——Gate对象,它是线程不安全的。在这个过程中,

                this.count++;
		this.name = name;
		this.country = address;
		Check();
是被三个线程交错调用的,要通过竞争来确定谁先执行。 可能出现一个线程在调用完pass的时候,恰好另一个线程 调用pass中给name赋值却还没及时给country赋值,结果前者check的时候发现name和country不一致,于是BROKEN,所以打印出来的名字和国家不一致。

注意到,在BROKEN中也有很多名字和国家一致的,这又是怎么回事?

同样因为Gate的线程不安全,前者在调用check的时候,后者调用pass将name和country同时修改了,于是又一致了。

如何让Gate线程安全呢?很简单,在被各个线程写数据的pass加上synchronized:

public synchronized void pass(String name,String address) {
		// TODO Auto-generated method stub
		this.count++;
		this.name = name;
		this.country = address;
		Check();
	}
结果 很给力~~
Testing Gate
BobeBEGIN
AliceBEGIN
ChenBEGIN
synchronized简单来说就是确保一个时刻只有一个线程访问对应的代码,其他线程在等待知道里面的线程执行完毕。而一般对于

多线程程序来说,对于多个线程同时执行的时候会使得对象的状态出现矛盾的方法,就要用synchronized来保护。synchronized实际上

是让线程得到一把锁(一般锁存在于对象中),只有持有锁的线程才可以执行,执行完synchronized代码块才释放锁。当一个线程执行的时候其他想执行

synchronized代码块的线程就需要等待这个锁。我们把这种当只有一个线程可以访问的代码称为临界区,这种多线程模式称为Single 

Thread Execution。
一般作为于方法和代码块:

public synchronized void method() {
		// TODO Auto-generated method stub
		
	}

            
synchronized(obj) {
		
	}

这时候锁住的是对象(方法所在的对象),于是当一个对象的 synchronized 代码有一个线程运行,其他线程也不能进入需要该对象其他代码块或方法。

还有一种是静态方法:

public static synchronized void method() {
		
	}

此时需持有对应类的class对象的锁。

java中另外一种锁是明锁:ReentrantLock。

比如上面的例子,pass方法改为:

private ReentrantLock lock = new ReentrantLock();
	
	public  void pass(String name,String address) {
		// TODO Auto-generated method stub
		lock.lock();
		this.count++;
		this.name = name;
		this.country = address;
		Check();
		lock.unlock();
	}

同样也是线程安全的。

相对来说,ReentrantLock更加安全,但是存在一些隐患。、

public  void method() {
		lock.lock();
		if(条件){
			return;//或者抛异常
		}
		lock.unlock();
		
	}
如果是进入if语句中,那么锁将永远打不开。那这一块代码其他线程就进不来了。 安全的方式是在finally中解锁。

public  void method() {
		lock.lock();
		try {
			if(条件){
				return;//或者抛异常
			}
		} finally{
			lock.unlock();
		}
	}

使用Single Thread Execution要注意的是避免死锁。死锁就是两个线程分别获得了锁定,互相等待另一个线程来解锁,最典型代码:

public void method1() {
		synchronized (A) {
			synchronized (B) {
				
			}
		}
	}
	
	public void method2() {
		synchronized (B) {
			synchronized (A) {
				
			}
		}
	}
当一个线程调用了method1,另一个线程同时调用了method2,两个线程都出不来,都在等待对方持有的那把锁。

Single Thread Execution达到以下条件就会出现死锁:

1.具有多个锁参与其中。

2.线程锁定一个锁没解除就去获得其他锁。

3.线程获取这些锁的顺序不同。

以上条件只要打破一个就可以避免死锁发生。











发布了69 篇原创文章 · 获赞 76 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/sinat_23092639/article/details/52549374
今日推荐