它是“一个线程”的意思,就像一个桥,每次只能通过一个人。
下面测试: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方法的时候要获取对象的锁定是需要时间的,当线程发生冲突的时候线程必须等待,这耗掉了很多时间。
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[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
这样一直出现等待。。。。。。