1. 前面几篇介绍了 synchronized方法、synchronized(this)代码块 两种线程同步。关键字 synchronized 还可以应用在 static 静态方法上,表示对当前的 *.java 文件对应的 Class 类进行持锁。
问题:Class类锁 和 object对象锁 是不是同一个锁?如果不是,有什么区别?
静态synchronized方法获得Class类锁,而synchronized方法获得object对象锁,看下面例子
public class Service {
/*
静态synchronized方法和 synchronized方法获得不同的锁,前者是Class锁,后者是 object对象锁
*/
public synchronized 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();
}
}
public synchronized static void printB() {
System.out.println("线程名为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 进入printB");
System.out.println("线程名为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 离开printB");
}
public synchronized void printC() {
System.out.println("线程名为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 进入printC");
System.out.println("线程名为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + " 离开printC");
}
}
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.printA();
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.printB();
}
}
public class ThreadC extends Thread {
private Service service;
public ThreadC(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.printC();
}
}
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 在 1542160264528 进入printA
线程名为:C 在 1542160264533 进入printC
线程名为:C 在 1542160264533 离开printC
线程名为:A 在 1542160267529 离开printA
线程名为:B 在 1542160267529 进入printB
线程名为:B 在 1542160267529 离开printB
A线程sleep的时候,C线程获得资源运行,而B线程要等待A线程执行完才能执行
*/
}
}
结果中可以看到,A、B 同步方法和 C同步方法是不同的锁。那么Class类锁 和 Object对象锁有什么区别呢?
前一篇(2)中第3点:多个对象锁的synchronized同步方法,不能保证线程同步。下面我们看一下 static synchronized方法在多个对象下是否能保证线程同步:
public class Run2 {
public static void main(String[] args) {
Service service = new Service();
Service service2 = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service2);
b.setName("B");
b.start();
/*运行结果:
线程名为:A 在 1542164041813 进入printA
线程名为:A 在 1542164044819 离开printA
线程名为:B 在 1542164044820 进入printB
线程名为:B 在 1542164044820 离开printB
可以看到:线程A和B是不同的对象,但是依然是 A线程执行完了B线程才执行,所以还是同步的。
说明 static synchronized方法并不是object对象锁,而是Class类锁。这也是 Class锁和Object对
象锁的不同点
*/
}
}
Java把内存分为栈内存和堆内存,其中栈内存用来存放一些基本类型的变量、数组和对象的引用,堆内存主要存放一些对象。在JVM加载一个类的时候,若该类存在static修饰的成员变量和成员方法,则会为这些成员变量和成员方法在固定的位置开辟一个固定大小的内存区域,有了这些“固定”的特性,那么JVM就可以非常方便地访问他们。同时如果静态的成员变量和成员方法不出作用域的话,它们的句柄都会保持不变。同时static所蕴含“静态”的概念表示着它是不可恢复的,即在那个地方,你修改了,他是不会变回原样的,你清理了,他就不会回来了。
同时被static修饰的成员变量和成员方法是独立于该类的,它不依赖于某个特定的实例变量,也就是说它被该类的所有实例共享。所有实例的引用都指向同一个地方,任何一个实例对其的修改都会导致其他实例的变化。
2. synchronized(Service.class) 代码块的作用和 synchronized static 方法作用一样。