# 简单介绍
#  synchronized  是 Java 中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号  {}  括起来的代码,作用的对象是调用这个代码块的对象;
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
  4. 修改一个类,其作用的范围是  synchronized  后面括号括起来的部分,作用主的对象是这个类的所有对象。
# 修饰一个代码块
1、一个线程访问一个对象中的  synchronized(this)  同步代码块时,其他试图访问该对象的线程将被阻塞。
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
   | package com.wbg;
  import java.util.ArrayList; import java.util.List;
  public class kt {     public static void main(String[] args) {         System.out.println("使用关键字synchronized");         SyncThread syncThread = new SyncThread();         Thread thread1 = new Thread(syncThread, "SyncThread1");         Thread thread2 = new Thread(syncThread, "SyncThread2");         thread1.start();         thread2.start();     } } class SyncThread implements Runnable {     private static int count;     public SyncThread() {         count = 0;     }     public  void run() {        synchronized (this){             for (int i = 0; i < 5; i++) {                 try {                     System.out.println("线程名:"+Thread.currentThread().getName() + ":" + (count++));                     Thread.sleep(100);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }         }     }     public int getCount() {         return count;     } }
   | 
 
# 使用关键字  synchronized  运行结果

# 不使用关键字  synchronized  运行结果

当两个并发线程 (  thread1  和  thread2  ) 访问同一个对象 (  syncThread  ) 中的  synchronized  代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。  Thread1  和  thread2  是互斥的,因为在执行  synchronized  代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。
我们再把  SyncThread  的调用稍微改一下:
1 2 3 4 5 6 7
   | public static void main(String[] args) {         System.out.println("使用关键字synchronized每次调用进行new SyncThread()");         Thread thread1 = new Thread( new SyncThread(), "SyncThread1");         Thread thread2 = new Thread( new SyncThread(), "SyncThread2");         thread1.start();         thread2.start();     }
  | 
 
# 运行结果

为什么上面的例子中  thread1  和  thread2  同时在执行。这是因为  synchronized  只锁定对象,每个对象只有一个锁(  lock  )与之相关联,而上面的代码等同于下面这段代码:
1 2 3 4 5 6 7 8 9
   | public static void main(String[] args) {         System.out.println("使用关键字synchronized每次调用进行new SyncThread()");         SyncThread syncThread1 = new SyncThread();         SyncThread syncThread2 = new SyncThread();         Thread thread1 = new Thread(syncThread1, "SyncThread1");         Thread thread2 = new Thread(syncThread2, "SyncThread2");         thread1.start();         thread2.start();     }
  | 
 
这时创建了两个  SyncThread  的对象  syncThread1  和  syncThread2  ,线程  thread1  执行的是  syncThread1  对象中的  synchronized  代码 (  run  ),而线程  thread2  执行的是  syncThread2  对象中的  synchronized  代码 (  run  );我们知道  synchronized  锁定的是对象,这时会有两把锁分别锁定  syncThread1  对象和  syncThread2  对象,而这两把锁是互不干扰的,不形成互斥,所以两个线程可以同时执行。
# 二、当一个线程访问对象的一个  synchronized(this)  同步代码块时,另一个线程仍然可以访问该对象中的非  synchronized(this)  同步代码块。
# 多个线程访问  synchronized  和非  synchronized  代码块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
   |  public static void main(String[] args) {         System.out.println("使用关键字synchronized");         Mthreads mt=new Mthreads();         Thread thread1 = new Thread(mt, "mt1");         Thread thread2 = new Thread(mt, "mt2");         thread1.start();         thread2.start();     } } class Mthreads implements Runnable{     private int count;
      public Mthreads() {         count = 0;     }
      public void countAdd() {         synchronized(this) {             for (int i = 0; i < 5; i ++) {                 try {                     System.out.println(Thread.currentThread().getName() + ":" + (count++));                     Thread.sleep(100);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }         }     }
           public void printCount() {         for (int i = 0; i < 5; i ++) {             try {                 System.out.println(Thread.currentThread().getName() + " count:" + count);                 Thread.sleep(100);             } catch (InterruptedException e) {                 e.printStackTrace();             }         }     }
      public void run() {         String threadName = Thread.currentThread().getName();         if (threadName.equals("mt1")) {             countAdd();         } else if (threadName.equals("mt2")) {             printCount();         }     }
  | 
 
# 运行结果

上面代码中  countAdd  是一个  synchronized  的,  printCount  是非  synchronized  的。从上面的结果中可以看出一个线程访问一个对象的  synchronized  代码块时,别的线程可以访问该对象的非  synchronized  代码块而不受阻塞。
# 修饰一个方法
Synchronized  修饰一个方法很简单,就是在方法的前面加  synchronized  ,  public synchronized void method(){}  ;   synchronized  修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。如将的  run  方法改成如下的方式,实现的效果一样。
1 2 3 4 5 6 7 8 9 10 11 12
   | public synchronized void run() {       {            for (int i = 0; i < 5; i++) {                try {                    System.out.println("线程名:"+Thread.currentThread().getName() + ":" + (count++));                    Thread.sleep(100);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }
  | 
 
运行结果

#  Synchronized  作用于整个方法的写法。
写法一:
1 2 3 4
   | public synchronized void method() {     }
   | 
 
写法二:
1 2 3 4 5 6
   | public void method() {    synchronized(this) {           } }
   | 
 
写法一修饰的是一个方法,写法二修饰的是一个代码块,但写法一与写法二是等价的,都是锁定了整个方法时的内容。
在用  synchronized  修饰方法时要注意以下要点:
 synchronized  关键字不能继承。
虽然可以使用  synchronized  来定义方法,但  synchronized  并不属于方法定义的一部分,因此,  synchronized  关键字不能被继承。如果在父类中的某个方法使用了  synchronized  关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上  synchronized  关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。这两种方式的例子代码如下:
在子类方法中加上  synchronized  关键字
1 2 3 4 5 6
   | lass Parent {    public synchronized void method() { } } class Child extends Parent {    public synchronized void method() { } }
  | 
 
在子类方法中调用父类的同步方法
1 2 3 4 5 6
   | class Parent {    public synchronized void method() {   } } class Child extends Parent {    public void method() { super.method();   } } 
  | 
 
在定义接口方法时不能使用 synchronized 关键字。
构造方法不能使用 synchronized 关键字,但可以使用 synchronized 代码块来进行同步。
# 修饰一个静态的方法
Synchronized  也可修饰一个静态方法,用法如下:
1 2 3
   | public synchronized static void method() {     }
  | 
 
我们知道静态方法是属于类的而不属于对象的。同样的,  synchronized  修饰的静态方法锁定的是这个类的所有对象。
#  synchronized  修饰静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
   | package com.wbg;
  import java.util.ArrayList; import java.util.List;
  public class kt {     public static void main(String[] args) {         System.out.println("使用关键字静态synchronized");         SyncThread syncThread = new SyncThread();         Thread thread1 = new Thread(syncThread, "SyncThread1");         Thread thread2 = new Thread(syncThread, "SyncThread2");         thread1.start();         thread2.start();     } } class SyncThread implements Runnable {     private static int count;
      public SyncThread() {         count = 0;     }
      public synchronized static void method() {         for (int i = 0; i < 5; i ++) {             try {                 System.out.println(Thread.currentThread().getName() + ":" + (count++));                 Thread.sleep(100);             } catch (InterruptedException e) {                 e.printStackTrace();             }         }     }
      public synchronized void run() {         method();     } }
   | 
 
# 运行结果

syncThread1  和  syncThread2  是  SyncThread  的两个对象,但在  thread1  和  thread2  并发执行时却保持了线程同步。这是因为  run  中调用了静态方法  method  ,而静态方法是属于类的,所以  syncThread1  和  syncThread2  相当于用了同一把锁。这与使用关键字  synchronized  运行结果相同
# 修饰一个类
Synchronized  还可作用于一个类,用法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
   | package com.wbg;
  import java.util.ArrayList; import java.util.List;
  public class kt {     public static void main(String[] args) {         System.out.println("使用ClassName");         SyncThread syncThread = new SyncThread();         Thread thread1 = new Thread(syncThread, "SyncThread1");         Thread thread2 = new Thread(syncThread, "SyncThread2");         thread1.start();         thread2.start();     } } class ClassName {     public void method() {         synchronized(ClassName.class) {
          }     } } class SyncThread implements Runnable {     private static int count;
      public SyncThread() {         count = 0;     }
      public static void method() {         synchronized(SyncThread.class) {             for (int i = 0; i < 5; i ++) {                 try {                     System.out.println(Thread.currentThread().getName() + ":" + (count++));                     Thread.sleep(100);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }         }     }
      public synchronized void run() {         method();     } }
   | 
 
# 运行结果

效果和上面  synchronized  修饰静态方法是一样的,  synchronized  作用于一个类  T  时,是给这个类  T  加锁,  T  的所有对象用的是同一把锁。
# 总结
1、 无论  synchronized  关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果  synchronized  作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
2、每个对象只有一个锁(  lock  )与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
3、实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制
原文链接:Java 中 Synchronized 的用法(简单介绍)