Java多线程编程核心技术(第3版)
上QQ阅读APP看书,第一时间看更新

2.1.5 synchronized方法将对象作为锁

为了证明前面讲的将对象作为锁,创建实验项目synchronizedMethodLockObject,类MyObject.java文件代码如下:


package extobject;

public class MyObject {

public void methodA() {
    try {
        System.out.println("begin methodA threadName="
            + Thread.currentThread().getName());
        Thread.sleep(5000);
        System.out.println("end");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

自定义线程类ThreadA.java代码如下:


package extthread;

import extobject.MyObject;

public class ThreadA extends Thread {

private MyObject object;

public ThreadA(MyObject object) {
    super();
    this.object = object;
}

@Override
public void run() {
    super.run();
    object.methodA();
}

}

自定义线程类ThreadB.java代码如下:


package extthread;

import extobject.MyObject;

public class ThreadB extends Thread {

private MyObject object;

public ThreadB(MyObject object) {
    super();
    this.object = object;
}

@Override
public void run() {
    super.run();
    object.methodA();
}
}

运行类Run.java代码如下:


package test.run;

import extobject.MyObject;
import extthread.ThreadA;
import extthread.ThreadB;

public class Run {

public static void main(String[] args) {
    MyObject object = new MyObject();
    ThreadA a = new ThreadA(object);
    a.setName("A");
    ThreadB b = new ThreadB(object);
    b.setName("B");

    a.start();
    b.start();
}

}

程序运行结果如图2-6所示。

图2-6 两个线程可一同进入methodA方法

两个线程可一同进入methodA方法,因为该方法并没有同步化。

更改MyObject.java代码如下:


package extobject;

public class MyObject {

synchronized public void methodA() {
    try {
        System.out.println("begin methodA threadName="
            + Thread.currentThread().getName());
        Thread.sleep(5000);
        System.out.println("end");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

如上面代码所示,在methodA方法前加入关键字synchronized进行同步处理。程序再次运行的结果如图2-7所示。

图2-7 排队进入方法

通过上面的示例得到结论,调用用关键字synchronized声明的方法一定是排队运行。另外,需要牢牢记住“共享”这两个字,只有共享资源的写访问才需要同步化,如果不是共享资源,那么就没有同步的必要。

那其他方法在被调用时会是什么效果呢?如何查看将对象作为锁的效果呢?继续新建实验项目synchronizedMethodLockObject2,类文件MyObject.java代码如下:


package extobject;

public class MyObject {

synchronized public void methodA() {
    try {
        System.out.println("begin methodA threadName="
            + Thread.currentThread().getName());
        Thread.sleep(5000);
        System.out.println("end endTime=" + System.currentTimeMillis());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public void methodB() {
    try {
        System.out.println("begin methodB threadName="
            + Thread.currentThread().getName() + " begin time="
            + System.currentTimeMillis());
        Thread.sleep(5000);
        System.out.println("end");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

两个自定义线程类分别调用不同的方法,代码如图2-8所示。

图2-8 调用不同方法的线程类

文件Run.java代码如下:


package test.run;

import extobject.MyObject;
import extthread.ThreadA;
import extthread.ThreadB;

public class Run {

public static void main(String[] args) {
    MyObject object = new MyObject();
    ThreadA a = new ThreadA(object);
    a.setName("A");
    ThreadB b = new ThreadB(object);
    b.setName("B");

    a.start();
    b.start();
}

}

程序运行结果如图2-9所示。

图2-9 线程B异步调用非同步方法

通过上面的实验可以得知,虽然线程A先持有了object对象的锁,但线程B完全可以异步调用非synchronized类型的方法。

继续实验,在MyObject.java文件中的methodB()方法前加入synchronized关键字,代码如下:


synchronized public void methodB() {
    try {
        System.out.println("begin methodB threadName="
            + Thread.currentThread().getName() + " begin time="
            + System.currentTimeMillis());
        Thread.sleep(5000);
        System.out.println("end");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

图2-10 同步运行

本示例是两个线程访问同一个对象的两个同步方法,运行结果如图2-10所示。

结论如下:

1)A线程先持有object对象的锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法。

2)A线程先持有object对象的锁,B线程如果在这时调用object对象中的synchronized类型的方法,则需等待,也就是同步。

3)在方法声明处添加synchronized并不是锁方法,而是锁当前类的对象。

4)在Java中只有将对象作为锁,并没有锁方法这种说法。

5)在Java语言中,锁就是对象,对象可以映射成锁,哪个线程拿到这把锁,哪个线程就可以执行这个对象中的synchronized同步方法。

6)如果在X对象中使用了synchronized关键字声明非静态方法,则X对象就被当成锁。