“做不到的承诺和撒谎没有什么区别。 ”
1.7线程同步
当多个线程共享同一个变量等资源时,需要确保资源在某一时刻只有一个线程占用,这个过程就是线程同步。信号量是同步中的一个重要概念。信号量是一个对象,也是互斥体。当一个线程进入互斥体,互斥体就会锁定,此时任何试图进入互斥体的线程都必须等待这个线程出来。
注:Java线程同步本身的复杂性决定了要正确处理线程同步不是一件容易的事情。
下面给出一个没有使用过线程同步的例子:
首先构造一个电话类,定义一个打电话的方法
public class PhoneCall {
public static void call(String name) {
try {
System.out.println(name + "拨打电话");
Thread.sleep(100);
System.out.println(name + "正在通话中");
Thread.sleep(100);
System.out.println(name + "挂断电话");
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
然后构造一个调用电话类打电话方法的线程类。看一看一步电话在没有同步情况下的工作状态:
public class Call extends Thread{
public Call(String arg0) {
super(arg0);
}
@Override
public void run() {
PhoneCall.call(getName());
}
}
最后在main方法里创建线程并调用:
public class Test {
public static void main(String[] args) {
Call first = new Call("First");
Call second = new Call("Second");
Call third = new Call("Third");
first.start();
second.start();
third.start();
}
}
执行结果为:
从执行结果可以看出,First打电话的等待过程中,Second也拨打了电话,Second进入等待时,Third开始拨打电话。而且First最后才通话。这是不合理的,所以需要用到线程同步。
同步实例:相同的场景
构造一个电话类,定义一个打电话的方法
/**
* @author asus
*/
public class PhoneCall {
public synchronized static void call(String name) {
try {
System.out.println(name + "拨打电话");
Thread.sleep(100);
System.out.println(name + "正在通话中");
Thread.sleep(100);
System.out.println(name + "挂断电话");
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
构造一个调用电话类打电话的线程类:
class SynCall extends Thread{
public SynCall(String arg0){
super(arg0);
}
@Override
public void run() {
PhoneCall.call(getName());
}
}
最后定义main方法:
/**
* @author asus
*/
public class Test {
public static void main(String[] args) {
SynCall first = new SynCall("First");
SynCall second = new SynCall("Second");
SynCall third = new SynCall("Third");
first.start();
second.start();
third.start();
}
}
执行结果如下:
First在拨打电话、通话、挂断电话后才能继续对Second进行下一操作这样才合理。 这个代码和上一个示例的代码,本质区别在于synchronized关键字。
此外使用synchronized(mutex){}同样可以实现同步。其中mutex就是互斥体,是一个对象。