注意:在理解synchronized时,要知道一个核心点,synchronized锁定的不是代码,而是对象。使用synchronized时,其会申请对象的堆内存,进行锁定。java
Object o = new Object(); // 锁对象 public void test01(){ //任何线程要执行如下的代码,必须先拿到锁 synchronized (o){ // doSomething... } }
上述写法是建立一个锁对象,其实能够自身做为锁对象。安全
public void test02(){ synchronized (this){ // doSomething... } }
同写法二。ide
public synchronized void test03(){ // doSomething... }
锁定静态方法。静态方法是属于类方法,没有对象。测试
public synchronized static void test04(){ }
同写法四this
public static void test04(){ synchronized (SynchronizeTest.class){ } }
演示代码:线程
public class RunnableDemo implements Runnable { private int count = 10; @Override public /*synchronized*/ void run() { count--; System.out.println(Thread.currentThread().getName() + "count=" + count); } public static void main(String[] args) { RunnableDemo t = new RunnableDemo(); for (int i = 0; i < 8; i++) { new Thread(t, "结果是:" + i).start(); } } }
结果是:调试
结果是:1count=9 结果是:0count=8 结果是:2count=7 结果是:3count=6 结果是:5count=5 结果是:4count=4 结果是:6count=3 结果是:7count=2
加入synchronized:code
结果是:0count=9 结果是:1count=8 结果是:2count=7 结果是:3count=6 结果是:4count=5 结果是:5count=4 结果是:6count=3 结果是:7count=2
能够看到输出顺序是不同的.对象
在实际业务场景中,每每将读方法不加锁,写的方法加锁,这样会致使一个问题,也就是读的数据是写以前的数据,致使脏读问题。解决方案就是,在读方法上也加锁。内存
加锁方法不影响不加锁方法的执行;
加锁方法访问另一个加锁方法,一个线程拥有某个对象的锁,再次申请的时候能够再次获得这把锁(至关于锁上了两把一样的锁);子类的同步方法调用父类的同步方法也能够;
synchronized 遇到异常,锁会被释放。若是不想该锁被释放,就直接catch;
public class StringAsSynchObject { private String stra = "hello"; private String strb = "hello"; void test1(){ synchronized (stra){ } } void test2(){ synchronized (strb){ } } }
在上述代码中,由于stra和strb 锁的是同一个对象,若是用到了同一个类库,在该类库中的代码锁定了字符串"hello",咱们读不到源码,而在业务代码中也锁定了一样的字符串,这就有可能会形成很是诡异的死锁阻塞。由于程序和类库不经意用了同一把锁。(这种状况通常没办法调试)。因此一般不要字符串做为锁定对象。
synchronize 锁释放的状况:
1)线程执行完毕;
2)线程发生异常;
3)线程进入休眠状态。wait()和notify()/notifyAll() 与 synchronize同时出现。