8. 静态同步synchronized方法与synchronized(class)代码块

关键字synchronized还可以应用static静态方法上,如果这样写,那是对当前的.java文件对应的Class类进行持锁。*

下面通过通过例子说明一下用法与效果。

(1) Service.java

public class Service {

	synchronized public static void printA() {
		try {
			System.out.println("线程名称为:" + Thread.currentThread().getName()
					+ "在" + System.currentTimeMillis() + "进入printA");
			Thread.sleep(3000);
			System.out.println("--线程名称为:" + Thread.currentThread().getName()
					+ "在" + System.currentTimeMillis() + "离开printA");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	synchronized public static void printB() {
		System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"
				+ System.currentTimeMillis() + "进入printB");
		System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"
				+ System.currentTimeMillis() + "离开printB");
	}

}

(2) ThreadA.java

public class ThreadA extends Thread {
	@Override
	public void run() {
		Service.printA();
	}
}

(3) ThreadB.java

public class ThreadB extends Thread {
	@Override
	public void run() {
		Service.printB();
	}
}

(4) Run.java

public class Run {

	public static void main(String[] args) {

		ThreadA a = new ThreadA();
		a.setName("A");
		a.start();

		ThreadB b = new ThreadB();
		b.setName("B");
		b.start();

	}

}
线程名称为:A在1634056855437进入printA
--线程名称为:A在1634056858458离开printA
线程名称为:B在1634056858458进入printB
线程名称为:B在1634056858459离开printB

从运行结果来看,并没有什么特别之处,都是同步的效果,和将synchronized关键字加到非static方法上使用的效果是一样的。其实还是有本质上的不同的,synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是对象上锁

为了证明不是同一个锁,我们可以通过下面的实验来验证:

(1) Service.java

public class Service {

	synchronized public static void printA() {
		try {
			System.out.println("线程名称为:" + Thread.currentThread().getName()
					+ "在" + System.currentTimeMillis() + "进入printA");
			Thread.sleep(3000);
			System.out.println("----线程名称为:" + Thread.currentThread().getName()
					+ "在" + System.currentTimeMillis() + "离开printA");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	synchronized public static void printB() {
		System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"
				+ System.currentTimeMillis() + "进入printB");
		System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"
				+ System.currentTimeMillis() + "离开printB");
	}

	synchronized public void printC() {
		System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"
				+ System.currentTimeMillis() + "进入printC");
		System.out.println("????线程名称为:" + Thread.currentThread().getName() + "在"
				+ System.currentTimeMillis() + "离开printC");
	}

}

(2) ThreadA.java

public class ThreadA extends Thread {
	private Service service;

	public ThreadA(Service service) {
		super();
		this.service = service;
	}

	@Override
	public void run() {
		service.printA();
	}

}

(3) ThreadB.java

public class ThreadB extends Thread {
	private Service service;

	public ThreadB(Service service) {
		super();
		this.service = service;
	}

	@Override
	public void run() {
		service.printB();
	}
}

(4) ThreadC.java

public class ThreadC extends Thread {

	private Service service;

	public ThreadC(Service service) {
		super();
		this.service = service;
	}

	@Override
	public void run() {
		service.printC();
	}
}

(5) Run.java

public class Run {

	public static void main(String[] args) {

		Service service = new Service();

		ThreadA a = new ThreadA(service);
		a.setName("A");
		a.start();

		ThreadB b = new ThreadB(service);
		b.setName("B");
		b.start();

		ThreadC c = new ThreadC(service);
		c.setName("C");
		c.start();
	}

}

执行结果:

线程名称为:A在1634056663156进入printA
线程名称为:C在1634056663157进入printC
????线程名称为:C在1634056663185离开printC
----线程名称为:A在1634056666186离开printA
线程名称为:B在1634056666186进入printB
线程名称为:B在1634056666186离开printB

实验结果说明:这个实验实际上设置了两个锁:

  • 对象锁:printC是非静态方法,受对象锁的约束
  • Class锁:PrintA、PrintB是静态方法,都被同一个Class约束

还可以将上述Service.java代码中的PrintB改为:

synchronized public void printB() {
    System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"
                       + System.currentTimeMillis() + "进入printB");
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"
                       + System.currentTimeMillis() + "离开printB");
}

重新运行,结果如下:

线程名称为:B在1634058460652进入printB
线程名称为:A在1634058460652进入printA
----线程名称为:A在1634058461681离开printA
线程名称为:B在1634058465681离开printB
线程名称为:C在1634058465681进入printC
????线程名称为:C在1634058465682离开printC

PrintB和PrintC都是非静态的,属于同一个锁,所以C要等待B结束后释放锁。

异步的原因是持有不同的锁,一个是对象锁,另外一个是Class锁,而Class锁对类的所有对象实例起作用。

同步synchronized(class)代码块的作用其实和synchronized static方法的作用一样。

通过下面的示例(省略Thread文件)验证这一点:

(1) Service.java

public class Service {

	public static void printA() {
		synchronized (Service.class) {
			try {
				System.out.println("线程名称为:" + Thread.currentThread().getName()
						+ "在" + System.currentTimeMillis() + "进入printA");
				Thread.sleep(3000);
				System.out.println("----线程名称为:" + Thread.currentThread().getName()
						+ "在" + System.currentTimeMillis() + "离开printA");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}

	public static void printB() {
		synchronized (Service.class) {
			System.out.println("线程名称为:" + Thread.currentThread().getName()
					+ "在" + System.currentTimeMillis() + "进入printB");
			System.out.println("++++线程名称为:" + Thread.currentThread().getName()
					+ "在" + System.currentTimeMillis() + "离开printB");
		}
	}
}

(2)Run.java

public class Run {

	public static void main(String[] args) {

		Service service1 = new Service();
		Service service2 = new Service();

		ThreadA a = new ThreadA(service1);
		a.setName("A");
		a.start();

		ThreadB b = new ThreadB(service2);
		b.setName("B");
		b.start();

	}

}

执行结果:

线程名称为:A在1634098725887进入printA
----线程名称为:A在1634098728911离开printA
线程名称为:B在1634098728911进入printB
++++线程名称为:B在1634098728912离开printB