Synchronized是经过监视器保证线程同步从而保证线程安全。可是Synchronized锁能够锁对象和锁类,并会产生不一样的效果,经过下面的案例完全理解Synchronized锁的使用方式。java
即:安全
对于普通的同步方法,锁是当前实例对象多线程
对于静态同步方法,锁是该类并发
对于同步方法块,锁是Synchronized括号里面配置的对象。ide
下面经过代码具体分析几种状况。要想了解并发状况,首先咱们必须知道,类信息、实例对象分别存放在什么位置。类的信息,包括静态变量都是存放在方法区中;而实例对象,包括类的成员变量,是存放在堆中。this
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
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,所以不会产生并发安全问题。同步
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,也不会产生线程安全问题。
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是两个线程执行两个实例化对象,添加的是对象锁,至关于各自的对象锁锁住各自的对象,而静态变量是类变量,存放在方法区中而不是堆中,此状况对象锁并不能保证线程同步,所以会产生线程安全问题。
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,两个线程执行两个实例化对象,因为使用的是类锁,也会使线程同步,保证线程安全。
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同样,添加对象锁,只能保证同一对象的并发同步,不能保证不一样对象同步。
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同样,添加类锁,不管是多个线程操做一个实例化对象仍是多个实例化对象,都能保证线程安全。
总结:
对象锁只能保证各自实例化对象并发的线程安全问题。类锁能够保证多个实例化多谢的安全问题。