synchronized静态同步方法与非静态同步方法锁的验证

/ 多线程 / 没有评论 / 324浏览

我们知道非静态synchronized同步方法的锁是当前对象,那么静态synchronized同步方法的锁到底是什么呢?在一上篇我们没有介绍这个锁到底是什么,但我们知道这个锁一定不是当前对象,否则静态synchronized同步方法和非静态同步方法在多线程执行时就应该是同步执行的,但执行结果确是异步的。(在上一篇有这样的事例)我们先不谈这个锁到底是什么,我们先通过下面的例子来验证一下非静态synchronized同步方法的锁是不是当前对象。

/**
* 用户登录
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:35
* @since 1.0.0
*/
public class Userinfo {

public synchronized void adminLogin() {
System.out.println(String.format("start: %s\tthread: %s", "admin", Thread.currentThread().getName()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("end: %s\tthread: %s", "admin", Thread.currentThread().getName()));
}

public void userLogin() {
synchronized (this) {
System.out.println(String.format("start: %s\tthread: %s", "user", Thread.currentThread().getName()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("end: %s\tthread: %s", "user", Thread.currentThread().getName()));
}
}

}
/**
* 管理用户请求
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:44
* @since 1.0.0
*/
public class RequestAdmin extends Thread {

private Userinfo userinfo;

public RequestAdmin(Userinfo userinfo) {
this.userinfo = userinfo;
}

@Override
public void run() {
userinfo.adminLogin();
}
}
/**
* 用户请求
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:44
* @since 1.0.0
*/
public class RequestUser extends Thread {

private Userinfo userinfo;

public RequestUser(Userinfo userinfo) {
this.userinfo = userinfo;
}

@Override
public void run() {
userinfo.userLogin();
}
}
/**
* 测试类
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:49
* @since 1.0.0
*/
public class RequestRun {
public static void main(String[] args) {
Userinfo userinfo = new Userinfo();
RequestAdmin requestAdmin = new RequestAdmin(userinfo);
RequestUser requestUser = new RequestUser(userinfo);
requestAdmin.start();
requestUser.start();
}
}
start: admin	thread: Thread-0
end: admin	thread: Thread-0
start: user	thread: Thread-1
end: user	thread: Thread-1

我们都加了synchronized同步关键字,但确有所区别。一个是用的synchronized同步方法,一个用的是synchronized同步代码块。我们知道synchronized同步代码块是要传一个参数的,这个参数就是synchronized同步代码块的锁。我们上述代码中传的是this也就是指当前对象。也就是当前对象是synchronized同步代码块的锁。如果程序执行是同步的那就说明synchronized同步方法的锁也是当前对象。如果程序执行是异步的那就说明synchronized同步方法的锁不是当前对象。如果我们将synchronized同步代码块的锁改成其它的对象那程序就会是异步执行的。

/**
* 用户登录
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:35
* @since 1.0.0
*/
public class Userinfo {

public synchronized void adminLogin() {
System.out.println(String.format("start: %s\tthread: %s", "admin", Thread.currentThread().getName()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("end: %s\tthread: %s", "admin", Thread.currentThread().getName()));
}

public void userLogin() {
synchronized (new Object()) {
System.out.println(String.format("start: %s\tthread: %s", "user", Thread.currentThread().getName()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("end: %s\tthread: %s", "user", Thread.currentThread().getName()));
}
}

}
start: user	thread: Thread-1
start: admin	thread: Thread-0
end: user	thread: Thread-1
end: admin	thread: Thread-0

综上所述,非静态synchronized方法的锁是当前对象。接下来我们用同样的方式来验证静态synchronized同步方法的锁不是当前对象。

/**
* 用户登录
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:35
* @since 1.0.0
*/
public class Userinfo {

public synchronized static void adminLogin() {
System.out.println(String.format("start: %s\tthread: %s", "admin", Thread.currentThread().getName()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("end: %s\tthread: %s", "admin", Thread.currentThread().getName()));
}

public static void userLogin(Object o) {
synchronized (o) {
System.out.println(String.format("start: %s\tthread: %s", "user", Thread.currentThread().getName()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("end: %s\tthread: %s", "user", Thread.currentThread().getName()));
}
}

}
/**
* 管理用户请求
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:44
* @since 1.0.0
*/
public class RequestAdmin extends Thread {

private Userinfo userinfo;

public RequestAdmin(Userinfo userinfo) {
this.userinfo = userinfo;
}

@Override
public void run() {
userinfo.adminLogin();
}
}
/**
* 用户请求
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:44
* @since 1.0.0
*/
public class RequestUser extends Thread {

private Userinfo userinfo;

public RequestUser(Userinfo userinfo) {
this.userinfo = userinfo;
}

@Override
public void run() {
userinfo.userLogin(userinfo);
}
}
/**
* 测试类
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:49
* @since 1.0.0
*/
public class RequestRun {
public static void main(String[] args) {
Userinfo userinfo = new Userinfo();
RequestAdmin requestAdmin = new RequestAdmin(userinfo);
RequestUser requestUser = new RequestUser(userinfo);
requestAdmin.start();
requestUser.start();
}
}
start: user	thread: Thread-1
start: admin	thread: Thread-0
end: user	thread: Thread-1
end: admin	thread: Thread-0

我们把代码做了一些修改我们把锁当成一个参数传入到了synchronized同步代码块中,虽然这个锁传递成功了,但程序执行还是异步的说明静态synchronized同步方法的锁不是当前对象。那静态synchronized同步方法锁到底是什么呢?答案就是当前对象的Class是静态synchronized同步方法的锁。我们看先下面的事例然后在具体说明它的原因。

/**
* 用户登录
*
* @author Sama
* @author admin@jilinwula.com
* @date 2017-03-15 10:35
* @since 1.0.0
*/
public class Userinfo {

public synchronized static void adminLogin() {
System.out.println(String.format("start: %s\tthread: %s", "admin", Thread.currentThread().getName()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("end: %s\tthread: %s", "admin", Thread.currentThread().getName()));
}

public static void userLogin(Object o) {
synchronized (o.getClass()) {
System.out.println(String.format("start: %s\tthread: %s", "user", Thread.currentThread().getName()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("end: %s\tthread: %s", "user", Thread.currentThread().getName()));
}
}

}
start: admin	thread: Thread-0
end: admin	thread: Thread-0
start: user	thread: Thread-1
end: user	thread: Thread-1

我们只做了一处修改就是我们不直接把传入的对象当做synchronized同步代码块的锁,而是获取传入对象的当前class。通过输出信息我们发现线程现在是同步执行的了,这就验证了我们上述所说的结论。静态synchronized同步方法的锁是当前对象的class。那为什么是class而不是当前对象呢。这是因为我们知道在Java中如果方法是静态方法,那么这个方法可以在没有实例化对象的时候就可直接调用,没有实例化也就是没有对象,连对象都没有怎么可以用当前对象当做静态同步方法的锁呢。但class不一样的,如果JVM成功加载了这个类就会自动创建class,不用考虑这个对象是否实例化。所以在Java中静态synchronized方法的锁是当前对象的class。事实上如果你直接在静态的synchronized同步代码块中用this当锁的话,编译都是通不过的。因为静态方法中根本就没有this对象。