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

来源:互联网 发布:剑三同人知乎 编辑:程序博客网 时间:2024/05/17 01:28

现在有三个人要循环随机通过一个门,三个人的名字和国家首字母都一样,每当一个人通过门的时候,就检查这个人和国家是否一致,不一致则报错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 stubthis.count++;this.name = name;this.country = address;Check();}public String toString() {return "No."+count+":"+name+","+country;}private void Check() {// TODO Auto-generated method stubif(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 stubSystem.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 stubthis.count++;this.name = name;this.country = address;Check();}
结果很给力~~
Testing GateBobeBEGINAliceBEGINChenBEGIN
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 stublock.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.线程获取这些锁的顺序不同。

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











1 0