一、代码示例
1.synchronized修饰普通方法
synchronized修饰普通代码,加锁对象为调用这个方法的对象
public class SynchronizedTest {
private int age;
public synchronized int getAge() throws InterruptedException {
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName()+"**"+System.currentTimeMillis());
return age;
}
public static void main(String []args){
SynchronizedTest synchronizedTest = new SynchronizedTest();
Thread thread1 = new Thread("t1"){
@Override
public void run(){
try {
synchronizedTest.getAge();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread2 = new Thread("t2"){
@Override
public void run(){
try {
synchronizedTest.getAge();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread1.start();
thread2.start();
}
}
因为2个线程调用getAge()方法的都是同一个对象synchronizedTest ,所以2个线程是串行进行,thread2会等待thread1
运行结果如下
t1**1561547808386
t2**1561547818390
这里说一点:最近在项目中,发现有人写了这样一段代码
synchronized (jedisPool) {
if(jedisPool == null){
initialPool();
}
}
大家发现里面错误了么
这里强调一点,因为Java对象只有一个内置锁,synchronized 会在这个内置锁进行加锁,我们平时定义对象为空类似
Object ob = null;
这个只是把对象指向一个空引用,并没有任何初始化,空在Java中是一种特殊类型,并不是对象。
如果代码中判断synchronized 加锁对象为空应该在synchronized 之前作出判断,如果加锁对象为空会报空指针异常。
这里我们再思考一个问题,如果多线程访问同一对象的不同的被synchronized修饰的普通方法会发生什么
public class SynchronizedTest6 {
public synchronized void getAge() throws InterruptedException {
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName()+"**"+System.currentTimeMillis());
}
public synchronized void test() throws InterruptedException {
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName()+"**"+System.currentTimeMillis());
}
public static void main(String []args){
SynchronizedTest6 synchronizedTest6 = new SynchronizedTest6();
Thread thread1 = new Thread("t1"){
@Override
public void run(){
try {
synchronizedTest6.getAge();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread2 = new Thread("t2"){
@Override
public void run(){
try {
synchronizedTest6.test();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread1.start();
thread2.start();
}
}
因为synchronized是加在对象内置锁上所以结果显而易见,多线程会串行进行,结果如下
t1**1561745488571
t2**1561745498572
2.synchronized修饰静态方法
synchronized修饰静态方法,锁会发生在这个静态方法类上,多个线程访问同一静态方法会串行执行
public class SynchronizedTest3 implements Runnable{
@Override
public void run() {
try {
test();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static synchronized void test() throws InterruptedException {
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName()+System.currentTimeMillis());
}
public static void main(String []args){
SynchronizedTest3 synchronizedTest3 = new SynchronizedTest3();
SynchronizedTest3 synchronizedTest31 = new SynchronizedTest3();
Thread thread1 = new Thread(synchronizedTest3);
Thread thread2 = new Thread(synchronizedTest31);
thread1.start();
thread2.start();
}
}
运行结果如下
Thread-01561744252006
Thread-11561744262008
3.synchronized修饰类
效果类同synchronized修饰静态方法
public class SynchronizedTest4 implements Runnable{
@Override
public void run() {
try {
test();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void test() throws InterruptedException {
synchronized (SynchronizedTest4.class){
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName()+System.currentTimeMillis());
}
}
public static void main(String []args){
SynchronizedTest4 synchronizedTest = new SynchronizedTest4();
SynchronizedTest4 synchronizedTest_ = new SynchronizedTest4();
Thread thread1 = new Thread(synchronizedTest);
Thread thread2 = new Thread(synchronizedTest_);
thread1.start();
thread2.start();
}
}
Thread-11561744702391
Thread-01561744712400
二、原理
在这里我们反编译下class,先看下字节码生成,这里推荐大家使用idea的插件jclasslib bytecode viewer查看字节码
同步代码块其实是通过monitorenter和monitorexit指令实现的,我们前面说了每个对象都有一把内置锁,这个锁其实是monitor(也被成为监视器锁),同一时间只能有一个对象获取到monitor,monitorenter指令会尝试获取monitor,而monitorexit会释放掉monitorexit。