synchronized锁

前言:

    在程序中,若存在多个线程同时操作共享变量,就会造成线程不安全,要保证多线程操作共享数据安全,必须加上互斥锁,同一时刻只有一个线程可以操作数据。

 

synchronized三种用法:

    1.实例方法

    2.静态方法

    3.代码块

我们先写出代码,查看运行结果,然后再分析

1.实例方法

package com.xhx.java;

/**
 * xuhaixing
 * 2018/6/30 17:29
 **/
public class SynchronizedMethod {
    public synchronized void method1(){
        System.out.println("method1--start");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("method1--end");
    }
    public synchronized void method2(){
        System.out.println("method2--start");
        System.out.println("method2--end");
    }

    public static void main(String[] args) {
        final SynchronizedMethod sync = new SynchronizedMethod();
        new Thread(() -> sync.method1()).start();
        new Thread(() -> sync.method2()).start();
    }
    /**
     * method1--start
     * method1--end
     * method2--start
     * method2--end
     */
}

可以看出,对于同一个实例,方法的执行也是互斥的,锁的时当前实例对象

synchronized锁_第1张图片

 

2.静态方法

package com.xhx.java;

/**
 * xuhaixing
 * 2018/6/30 17:29
 **/
public class SynchronizedStaticMethod {
    public static synchronized void method1(){
        System.out.println("method1--start");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("method1--end");
    }
    public static synchronized void method2(){
        System.out.println("method2--start");
        System.out.println("method2--end");
    }

    public static void main(String[] args) {
        new Thread(() ->
                SynchronizedStaticMethod.method1()).start();
        new Thread(() ->
                SynchronizedStaticMethod.method2()).start();
    }
    /**
     * method1--start
     * method1--end
     * method2--start
     * method2--end
     */
}

类中的静态方法执行也是互斥的,锁的是Class

synchronized锁_第2张图片

 

3.代码块

package com.xhx.java;

/**
 * xuhaixing
 * 2018/6/30 17:29
 **/
public class SynchronizedBlock {
    public void method1() {
        System.out.println("method1--start");
        try {
            synchronized (this) {
                System.out.println("method1--executing");
                Thread.sleep(3000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("method1--end");
    }

    public void method2() {
        System.out.println("method2--start");
        synchronized (this) {
            System.out.println("method2--executing");
        }
        System.out.println("method2--end");
    }

    public static void main(String[] args) {
        final SynchronizedBlock sync = new SynchronizedBlock();
        new Thread(() -> sync.method1()).start();
        new Thread(() -> sync.method2()).start();
    }
    /**
     method1--start
     method1--executing
     method2--start
     method1--end
     method2--executing
     method2--end
     */
}

执行到锁时,因为锁的都是this,所以代码块也是互斥的

synchronized锁_第3张图片

 

有一种情况需要注意,如果线程A访问静态的synchronized方法,线程B访问非静态的synchronized方法,是允许的,因为它俩用的不是同一个锁。

在使用synchronized的时候,尽量降低加锁范围,能在代码段上加锁,就不要在方法上加锁。synchronized内部通过monitor来实现的,属于重量级锁。

 

Synchronized原理

          每一个对象有一个监视器(monitor),当monitor被占用时就会处于锁定状态。

monitorenter:

线程执行monitorenter指令时会尝试获取monitor的所有权,如果monitor的进入数为1,则该线程进入monitor,然后将其设置为1,该线程即为monitor的所有者;如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1;如果其他线程占有了monitor,则该线程处于阻塞状态,知道monitor的进入数为0,再重新尝试获取monitor的所有权。

 

monitorexit:

执行monitorexit的线程必须是objectref所对应的monitor的所有者,当指令执行时,monitor的进入数会减一,如果减一后进入数为0,则线程退出monitor,不再是这个monitor的所有者,其他阻塞线程去获取这个monitor的所有权。

wait/notify等方法依赖于monitor对象,这就是为什么只有再同步代码块或者同步方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException异常。

 

 

我的github代码地址

 

你可能感兴趣的:(java)