扫码关注微信公众号

回复“面试手册”,获取本站PDF版

回复“简历”,获取高质量简历模板

回复“加群”,加入程序员交流群

回复“电子书”,获取程序员类电子书

当前位置: Java > Java并发高频面试题 > 34.synchronized关键字的使用方法

synchronized主要有三种使用方式:修饰普通同步方法、修饰静态同步方法、修饰同步方法块。

  • 修饰普通同步方法(实例方法)
class syncTest implements Runnable {

    private static int i = 0;   //共享资源

    private synchronized void add() {
        i++;
    }

    @Override
    public void run() {
        for (int j = 0; j < 10000; j++) {
            add();
        }
    }

    public static void main(String[] args) throws Exception {

        syncTest syncTest = new syncTest();

        Thread t1 = new Thread(syncTest);
        Thread t2 = new Thread(syncTest);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(i);
    }
}

这是一个非常经典的例子,多个线程操作i++会出现线程不安全问题,这段代码的结果很容易得到

20000

大家可以再看看这段代码,猜一猜它的运行结果

class syncTest implements Runnable {

    private static int i = 0;   //共享资源

    private synchronized void add() {
        i++;
    }

    @Override
    public void run() {
        for (int j = 0; j < 10000; j++) {
            add();
        }
    }

    public static void main(String[] args) throws Exception {

//        syncTest syncTest = new syncTest();

        Thread t1 = new Thread(new syncTest());
        Thread t2 = new Thread(new syncTest());

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(i);
    }
}

结果为

18634

第二个示例中的add()方法虽然也使用synchronized关键字修饰了,但是因为两次new syncTest()操作建立的是两个不同的对象,也就是说存在两个不同的对象锁,线程t1和t2使用的是不同的对象锁,所以不能保证线程安全。那这种情况应该如何解决呢?因为每次创建的实例对象都是不同的,而类对象却只有一个,如果synchronized关键字作用于类对象,即用synchronized修饰静态方法,问题则迎刃而解。

  • 修饰静态方法

只需要在add()方法前用static修饰即可,即当synchronized作用于静态方法,锁就是当前的class对象。

class syncTest implements Runnable {

    private static int i = 0;   //共享资源

    private static synchronized void add() {
        i++;
    }

    @Override
    public void run() {
        for (int j = 0; j < 10000; j++) {
            add();
        }
    }

    public static void main(String[] args) throws Exception {

//        syncTest syncTest = new syncTest();

        Thread t1 = new Thread(new syncTest());
        Thread t2 = new Thread(new syncTest());

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(i);
    }
}

结果为

20000
  • 修饰同步代码代码块

如果某些情况下,整个方法体比较大,需要同步的代码只是一小部分,如果直接对整个方法体进行同步,会使得代码性能变差,这时只需要对一小部分代码进行同步即可。代码如下:

class syncTest implements Runnable {

    static int i = 0;   //共享资源

    @Override
    public void run() {
        //其他操作.......
        synchronized (this){   //this表示当前对象实例,这里还可以使用syncTest.class,表示class对象锁
            for (int j = 0; j < 10000; j++) {
                i++;
            }
        }

    }

    public static void main(String[] args) throws Exception {

        syncTest syncTest = new syncTest();

        Thread t1 = new Thread(syncTest);
        Thread t2 = new Thread(syncTest);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(i);
    }
}

输出结果:

20000

点击面试手册,获取本站面试手册PDF完整版