在多线程编程中,线程同步是一个非常重要的概念。它确保了在多线程环境中,共享资源的访问是有序的,避免了数据的不一致性和竞态条件。在Java中,可以通过对象锁来实现线程同步。对象锁是一种内置的同步机制,它允许多个线程在访问共享资源时进行协调。
在之前的示例中,创建了两个线程t1和t2,它们都是从同一个处理器对象p1创建的,并使用对象锁objLock进行同步。这个对象锁是处理器类的一个成员变量,并且是p1的一部分。因此,t1和t2都可以访问同一个objLock,因为它们都是从p1创建的,如下代码片段所示:
processor p1 = new processor();
Thread t1 = new Thread(p1, "t1");
Thread t2 = new Thread(p1, "t2");
如果尝试从一个新的处理器对象p2创建t2,程序会是什么样子呢?程序看起来会是这样:
class processor implements Runnable {
Object obj;
public processor() {
obj = new Object();
}
public void run() {
display();
}
public void display() {
synchronized(obj) {
for (int i = 0; i <= 5; i++) {
System.out.println("i = " + i + " In thread" + Thread.currentThread());
}
}
}
}
class locks {
public static void main(String[] args) {
processor p1 = new processor();
processor p2 = new processor();
Thread t1 = new Thread(p1, "t1");
Thread t2 = new Thread(p2, "t2");
t1.start();
t2.start();
}
}
能猜到程序的输出是什么吗?会是常规输出,还是不规则的交错输出,充满了t1和t2的printfs,然后是t1,然后是t2,如此反复?
上述程序编译并成功运行,但得到的输出是这样的:
i = 0 In threadThread[t2, 5, main]
i = 0 In threadThread[t1, 5, main]
i = 1 In threadThread[t1, 5, main]
i = 1 In threadThread[t2, 5, main]
i = 2 In threadThread[t1, 5, main]
i = 2 In threadThread[t2, 5, main]
i = 3 In threadThread[t1, 5, main]
i = 3 In threadThread[t2, 5, main]
i = 4 In threadThread[t1, 5, main]
i = 4 In threadThread[t2, 5, main]
i = 5 In threadThread[t1, 5, main]
i = 5 In threadThread[t2, 5, main]
如果仔细观察,得到了一个交错的、不规则的输出。如果有对象锁,为什么会得到交错输出呢?答案很简单。确实有对象锁。但在这种情况下,p1的objectLock和p2的objectLock是不同的,不是共享的。处理器类创建的p1对象有自己的objectLock实例,这与p2的objectLock完全不同,因为p1和p2现在是两个不同的对象。没有竞争条件去获取同一个锁。
线程t2获取它自己的锁,线程t1获取它自己的锁,它们继续执行。因此,得到了交错输出。
那么,如何得到同步输出呢?为了得到同步输出,需要确保两个线程都有访问同一个锁。这在以下示例中实现:
class processor implements Runnable {
Object objLock;
public processor(Object objLock) {
this.objLock = objLock;
}
public void run() {
display();
}
public void display() {
synchronized(objLock) {
for (int i = 0; i <= 5; i++) {
System.out.println("i = " + i + " In thread" + Thread.currentThread());
}
}
}
}
class locks {
public static void main(String[] args) {
Object objLock = new Object();
processor p1 = new processor(objLock);
processor p2 = new processor(objLock);
Thread t1 = new Thread(p1, "t1");
Thread t2 = new Thread(p2, "t2");
t1.start();
t2.start();
}
}
输出:
i = 0 In threadThread[t1, 5, main]
i = 1 In threadThread[t1, 5, main]
i = 2 In threadThread[t1, 5, main]
i = 3 In threadThread[t1, 5, main]
i = 4 In threadThread[t1, 5, main]
i = 5 In threadThread[t1, 5, main]
i = 0 In threadThread[t2, 5, main]
i = 1 In threadThread[t2, 5, main]
i = 2 In threadThread[t2, 5, main]
i = 3 In threadThread[t2, 5, main]
i = 4 In threadThread[t2, 5, main]
i = 5 In threadThread[t2, 5, main]
上述程序编译并成功运行,产生了所需的同步输出。为了使线程t1和t2有相同的objLock,在main()中创建了它,然后通过构造函数将其传递给处理器对象p1和p2。现在p1和p2共享同一个objLock。在display()方法的for循环中的竞争条件期间,t1获取objLock的锁。因此,t2必须等待t1释放锁。t2只有在t1释放锁后才有机会进入for循环。这样,得到了同步输出。
希望这清楚地解释了对象锁是如何工作的。两个或更多的线程将同步,如果它们有一个且只有一个共同的对象锁。