Java

synchronized 用法

2019-07-04  本文已影响19人  endlesswork

一、代码示例

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){
        SynchronizedTest3 synchronizedTest3 = new SynchronizedTest3();
        SynchronizedTest3 synchronizedTest31 = new SynchronizedTest3();
        Thread thread1 = new Thread(synchronizedTest3);
        Thread thread2 = new Thread(synchronizedTest31);
        thread1.start();
        thread2.start();

    }
}
Thread-11561744702391
Thread-01561744712400

二、原理

在这里我们反编译下class,先看下字节码生成,这里推荐大家使用idea的插件jclasslib bytecode viewer查看字节码

image.png

同步代码块其实是通过monitorenter和monitorexit指令实现的,我们前面说了每个对象都有一把内置锁,这个锁其实是monitor(也被成为监视器锁),同一时间只能有一个对象获取到monitor,monitorenter指令会尝试获取monitor,而monitorexit会释放掉monitorexit。

上一篇 下一篇

猜你喜欢

热点阅读