静态synchronized同步方法

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

我们知道synchronized关键字是可以修饰方法和代码块的,但如果用synchronized修饰静态(static)方法会有什么不同呢。我们看下面的例子。

/**
* 用户登录
*
* @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 synchronized static void userLogin() {
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关键字去掉在执行上面的代码会出现什么结果呢?我们继续看下面的代码。

/**
* 用户登录
*
* @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() {
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
start: user	thread: Thread-1
end: user	thread: Thread-1
end: admin	thread: Thread-0

我们看输出显示结果显示变成异步执行了,这是为什么呢。其实道理和上面的一样,就是线程只给synchronized关键字上锁,而没有用synchronized关键字修饰的方法是没有锁的,所以其它线程也是可以直接执行的。所以就会出线上述的执行结果。那么我继续修改代码,这次我们添加synchronized关键字但把static静态关键字去掉,在执行一次程序结果会是什么样呢?我们先分析一下,我们知道如果方法添加了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 synchronized void userLogin() {
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
start: user	thread: Thread-1
end: user	thread: Thread-1
end: admin	thread: Thread-0

我们看输出结果显示执行结果居然还是异步执行的,这又是什么原因呢?我们在分析一下,如果方法添加了synchronized关键字那么线程在执行时一定是要获取锁的,但我们看输出结果显示是异步执行的,这就说明线程一执行方法时获取到了锁然后执行方法里的代码,这时线程二也进来了于是它也要获取锁,但这里和以往不同的是虽然方法添加了synchronized关键字,但线程二居然也成功的获取到了锁。所以线程二也执行了synchronized方法里的代码。所以就会造成上述代码异步执行的结果。虽然我们分析出来的原因,那它的本质是什么呢,为什么会重新获取到锁呢?那么真相只有一个,它们的锁不是同一个锁。也就是说静态synchronized同步方法和非静态synchronized同步方法的锁不是同一个锁。所以可以重新获取,这也是静态同步和非静态同步的区别,我们知道非静态synchronized方法的锁是当前对象,那静态方法的锁到底是什么呢?我们在下在篇中来重点讲解这方面的知识。谢谢。