synchronized关键字的做用是线程同步,而线程的同步是为了防止多个线程访问一个数据对象时,对数据形成的破坏。java
synchronized public void getValue() { ... }
上面的代码修饰的synchronized是非静态方法,若是修饰的是静态方法(static)含义是彻底不同的。具体不同在哪里,后面会详细说清楚。 ide
synchronized static public void getValue() { ... }
public void synchronizedMethod() { try { synchronized (this) { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } } catch (InterruptedException e) { e.printStackTrace(); } }
上面的代码块是synchronized (this)用法,还有synchronized (非this对象)以及synchronized (类.class)这两种用法,这些使用方式的含义也是有根本的区别的。咱们先带着这些问题继续往下看。性能
synchronized关键字的使用大体有五种状况,其中三种是对象锁,两种是类锁:this
下面看一些例子,首先看一下线程不一样步的状况:spa
public class SynchronizedTest { public void synchronizedMethod() { try { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } } public class MyThread extends Thread { private SynchronizedTest synchronizedTest; public MyThread(SynchronizedTest synchronizedTest) { super(); this.synchronizedTest = synchronizedTest; } @Override public void run() { super.run(); synchronizedTest.synchronizedMethod(); } } public class Main { public static void main(String[] args) throws InterruptedException { SynchronizedTest synchronizedTest1 = new SynchronizedTest(); Thread a = new MyThread(synchronizedTest1); a.setName("a"); a.start(); Thread b = new MyThread(synchronizedTest1); b.setName("b"); b.start(); } }
运行结果:线程
Thread[a,5,main]begin at:2017-09-13 16:52:54 Thread[b,5,main]begin at:2017-09-13 16:52:54 Thread[a,5,main]end at:2017-09-13 16:52:56 Thread[b,5,main]end at:2017-09-13 16:52:56
能够看到两个线程交叉执行,要让这两个线程依次执行,则须要使用对象锁同步,能够将SynchronizedTest类修改为下面的三种方式来添加对象锁:code
public class SynchronizedTest { synchronized public void synchronizedMethod() { try { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } } 或者 public class SynchronizedTest { public void synchronizedMethod() { try { synchronized (this) { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } } catch (InterruptedException e) { e.printStackTrace(); } } } 或者 public class SynchronizedTest { Object object = new Object(); public void synchronizedMethod() { try { synchronized (object) { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } } catch (InterruptedException e) { e.printStackTrace(); } } }
运行结果:orm
Thread[a,5,main]begin at:2017-09-13 16:59:12 Thread[a,5,main]end at:2017-09-13 16:59:14 Thread[b,5,main]begin at:2017-09-13 16:59:14 Thread[b,5,main]end at:2017-09-13 16:59:16
从上面能够看出,synchronized代码块(后两种方式)使用起来比synchronized方法(第一种方式)要灵活得多。由于也许一个方法中只有一部分代码只须要同步,若是此时对整个方法用synchronized进行同步,会影响程序执行效率。而使用synchronized代码块就能够避免这个问题,synchronized代码块能够实现只对须要同步的地方进行同步。 对象
若是将Main类修改为下面这样,则对象锁失效:blog
public class Main { public static void main(String[] args) throws InterruptedException { SynchronizedTest synchronizedTest1 = new SynchronizedTest(); SynchronizedTest synchronizedTest2 = new SynchronizedTest(); Thread a = new MyThread(synchronizedTest1); a.setName("a"); a.start(); Thread b = new MyThread(synchronizedTest2); b.setName("b"); b.start(); } }
运行结果:
Thread[b,5,main]begin at:2017-09-13 17:03:26 Thread[a,5,main]begin at:2017-09-13 17:03:26 Thread[b,5,main]end at:2017-09-13 17:03:28 Thread[a,5,main]end at:2017-09-13 17:03:28
由于上面两个线程调用的是两个对象中的方法,对象锁是不起做用的,这种状况下应该使用类锁,能够将SynchronizedTest类修改为下面的两种方式来添加类锁:
public class SynchronizedTest { public void synchronizedMethod() { try { synchronized (SynchronizedTest.class) { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } } catch (InterruptedException e) { e.printStackTrace(); } } } 或者 public class SynchronizedTest { synchronized public static void synchronizedMethod() { try { System.out.println(Thread.currentThread()+"begin at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); Thread.sleep(2000); System.out.println(Thread.currentThread()+"end at:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } }
运行结果:
Thread[a,5,main]begin at:2017-09-13 17:07:02 Thread[a,5,main]end at:2017-09-13 17:07:04 Thread[b,5,main]begin at:2017-09-13 17:07:04 Thread[b,5,main]end at:2017-09-13 17:07:06
须要特别说明:
对于同一个类A,线程1争夺A对象实例的对象锁,线程2争夺类A的类锁,这二者不存在竞争关系。也就说对象锁和类锁互不干预。
静态方法则必定会同步,非静态方法需在单例模式才生效,可是也不能都用静态同步方法,总之用得很差可能会给性能带来极大的影响。另外,有必要说一下的是Spring的bean默认是单例的。