今后不怕Synchronized锁

  • Synchronized的使用

​ Synchronized是经过监视器保证线程同步从而保证线程安全。可是Synchronized锁能够锁对象和锁类,并会产生不一样的效果,经过下面的案例完全理解Synchronized锁的使用方式。java

即:安全

对于普通的同步方法,锁是当前实例对象多线程

对于静态同步方法,锁是该类并发

对于同步方法块,锁是Synchronized括号里面配置的对象。ide

下面经过代码具体分析几种状况。要想了解并发状况,首先咱们必须知道,类信息、实例对象分别存放在什么位置。类的信息,包括静态变量都是存放在方法区中;而实例对象,包括类的成员变量,是存放在堆中。this

1. 成员变量+普通同步方法+锁

public class SynDemo implements Runnable {

    private int sum = 0;

    @Override
    public void run() {
        add();
    }

    public synchronized void add() {
        for (int i = 0; i < 1000; i++) {
            sum++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();		//等待线程执行完
        thread2.join();		//等待线程执行完
        System.out.println(sync01.sum);
        System.out.println(new SynDemo().sum);
    }
}

result:
	2000
        0
public class SynDemo implements Runnable {

    private int sum = 0;

    @Override
    public void run() {
        add();
    }

    public synchronized void add() {
        for (int i = 0; i < 1000; i++) {
            sum++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();		//等待线程执行完
        thread2.join();		//等待线程执行完
        System.out.println(sync01.sum);
        System.out.println(sync02.sum);
    }
}

result:
	1000
        1000
        0

分析:线程

​ 理解了这两个demo再去理解同步代码块下的多线程安全问题,将会达到事半功倍的效果。上面两个demo主要是想表达,成员变量和类的实例化对象同样,都是在堆中建立,每次new对象,都会在堆中产生一个新的对象。因此第一个demo中,当在线程同步的状况下,两个线程去操做同一个对象,最后的结果是2000;而第二个demo中,两个线程去操做两个实例化对象,因此每一个对象的成员变量sum为1000。所以咱们也能够发现,其实在第一个demo中才会有线程安全问题,在第二个demo中是不存在线程安全问题的,有疑问能够去掉锁验证一下。经过这个例子也能够去理解为何sping中多例是线程安全的。code

2. 成员变量+同步代码块+对象锁

public class SynDemo implements Runnable {

    private int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (this) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();  //等待线程执行完
        thread2.join();  //等待线程执行完
        System.out.println(sync01.sum);
        System.out.println(new SynDemo().sum);
    }
}

result:
	2000
        0
public class SynDemo implements Runnable {

    private int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (this) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();  //等待线程执行完
        thread2.join();  //等待线程执行完
        System.out.println(sync01.sum);
        System.out.println(sync02.sum);
        System.out.println(new SynDemo().sum);
    }
}

result:
	1000
	1000
	0

分析:对象

​ 同案例1同样,Demo1为两个线程执行一个实例化对象,可是加了Synchronized对象锁,所以实现了同步,保证线程安全。Demo2为两个线程执行两个实例化对象,各自利用各自的成员变量sum,所以不会产生并发安全问题。同步

3. 成员变量+同步代码块+类锁

public class SynDemo implements Runnable {

    private int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (SynDemo.class) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();  //等待线程执行完
        thread2.join();  //等待线程执行完
        System.out.println(sync01.sum);
        System.out.println(new SynDemo().sum);
    }
}

result:
	2000
	0
public class SynDemo implements Runnable {

    private int sum = 0;

    @Override
    public void run() {

        add();
    }

    private void add() {
        synchronized (SynDemo.class) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();  //等待线程执行完
        thread2.join();  //等待线程执行完
        System.out.println(sync01.sum);
        System.out.println(sync02.sum);
        System.out.println(new SynDemo().sum);
    }
}

result:
	1000
	1000
	0

分析:

​ Demo1为两个线程执行一个实例化对象,会产生并发安全问题,可是加了同步类锁(能够理解为锁的级别比对象锁更高),固然也能够实现并发同步,保证线程安全。而Demo2一样实例化两个对象,各自操做各自的成员变量sum,也不会产生线程安全问题。

4. 静态变量+普通方法+锁

public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    private synchronized void add() {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();  //等待线程执行完
        thread2.join();  //等待线程执行完
        System.out.println(sum);
    }
}

result:
	2000
public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    private synchronized void add() {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();  //等待线程执行完
        thread2.join();  //等待线程执行完
        System.out.println(sum);
    }
}

输出结果不肯定(存在线程安全问题)

分析:

​ 从案例4咱们要注意,由成员变量换成静态变量,而上面已经讲过,静态变量存放在方法区中,全部实例化对象共享一份。再看Demo1,两个线程执行同一个实例化对象,而后添加的是对象锁,所以该对象锁能锁住该实例化对象,实现同步,保证线程安全。

​ Demo2是两个线程执行两个实例化对象,添加的是对象锁,至关于各自的对象锁锁住各自的对象,而静态变量是类变量,存放在方法区中而不是堆中,此状况对象锁并不能保证线程同步,所以会产生线程安全问题。

5. 静态变量+静态方法+锁

public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    static synchronized void add() {
        for (int i = 0; i < 1000; i++) {
            sum++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();  //等待线程执行完
        thread2.join();  //等待线程执行完
        System.out.println(sum);
    }
}

result:
	2000
public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    static synchronized void add() {
        for (int i = 0; i < 1000; i++) {
            sum++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();  //等待线程执行完
        thread2.join();  //等待线程执行完
        System.out.println(sum);
    }
}

result:
	2000

分析:

​ 该案例相比案例4,锁由对象锁换成类锁,对于Demo1,两个线程操做一个对象,毫无疑问会使其同步。而Demo2,两个线程执行两个实例化对象,因为使用的是类锁,也会使线程同步,保证线程安全。

6. 静态变量+同步代码块+对象锁

public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (this) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();  //等待线程执行完
        thread2.join();  //等待线程执行完
        System.out.println(sum);
    }
}

result:
	2000
public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (this) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();  //等待线程执行完
        thread2.join();  //等待线程执行完
        System.out.println(sum);
    }
}

输出结果不肯定(存在线程安全问题)

分析:该案例和案例4同样,添加对象锁,只能保证同一对象的并发同步,不能保证不一样对象同步。

7. 静态变量+同步代码块+类锁

public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (SynDemo.class) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync01);
        thread1.start();
        thread2.start();
        thread1.join();  //等待线程执行完
        thread2.join();  //等待线程执行完
        System.out.println(sum);
    }
}

result:
	2000
public class SynDemo implements Runnable {

    private static int sum = 0;

    @Override
    public void run() {
        add();
    }

    private void add() {
        synchronized (SynDemo.class) {
            for (int i = 0; i < 1000; i++) {
                sum++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynDemo sync01 = new SynDemo();
        SynDemo sync02 = new SynDemo();
        Thread thread1 = new Thread(sync01);
        Thread thread2 = new Thread(sync02);
        thread1.start();
        thread2.start();
        thread1.join();  //等待线程执行完
        thread2.join();  //等待线程执行完
        System.out.println(sum);
    }
}

result:
	2000

分析:

​ 该案例同案例5同样,添加类锁,不管是多个线程操做一个实例化对象仍是多个实例化对象,都能保证线程安全。

总结:

对象锁只能保证各自实例化对象并发的线程安全问题。类锁能够保证多个实例化多谢的安全问题

相关文章
相关标签/搜索