Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只能有一个线程获得执行。另外一个线程必须等待当前线程执行完这个代码块之后才能执行该代码块。然而,当一个线程访问object的一个加锁代码块时,另外一个线程仍然能够访问该object中的非加锁代码块。java
1、synchronized同步方法并发
1.synchronized 同步方法,为对象锁ide
public class ExceptionReleaseLock { public synchronized void testLockException(){ if (Thread.currentThread().getName().equals("A")){ System.out.println("线程名字为:"+Thread.currentThread().getName()+" 开始运行时间:"+System.currentTimeMillis()); for(int i=0;i<200;i++){ while(i>20){ System.out.println("i="+i); Integer.parseInt("abcd"); } } }else{ System.out.println("B线程运行时间:"+System.currentTimeMillis()); } } }
2.synchronized 锁重入测试
当线程请求一个由其它线程持有的对象锁时,该线程会阻塞,而当线程请求由本身持有的对象锁时,若是该锁是重入锁,请求就会成功,不然阻塞.this
咱们来看看synchronized,它拥有强制原子性的内置锁机制,是一个重入锁,因此在使用synchronized时,当一个线程请求获得一个对象锁后再次请求此对象锁,能够再次获得该对象锁,就是说在一个synchronized方法/块的内部调用本类的其余synchronized方法/块时,是永远能够拿到锁。不然,就会形成死锁。spa
/** * Created by Administrator on 2015/12/30 0030. * * 锁重入机制 */public class LockSyn { public synchronized void a(){ System.out.println("a()"); b(); } public synchronized void b(){ System.out.println("b()"); c(); } public synchronized void c(){ System.out.println("c()"); } }
public class TestLockSyn { public static void main(String[] args){ new LockSyn().a(); } }
输出结果:线程
a() b() c()
当存在继承关系时,子类能够经过可重入锁调用父类的同步方法。code
public class Animal { synchronized public void eat(){ System.out.println("Animal eat foods"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Cat extends Animal { synchronized public void eatFish(){ System.out.println("cat eatFish..."); try { Thread.sleep(2000); this.eat(); } catch (InterruptedException e) { e.printStackTrace(); } } }
测试:orm
public class TestAnimal { public static void main(String[] args){ Cat cat = new Cat(); cat.eatFish(); } }
输出:对象
cat eatFish... Animal eat foods
咱们再来看看重入锁是怎么实现可重入性的,其实现方法是为每一个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程均可能得到该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,而且将计数器置为1;此时其它线程请求该锁,则必须等待;而该持有锁的线程若是再次请求这个锁,就能够再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,若是计数器为0,则释放该锁。
3. 发生异常,锁自动释放
public class ExceptionReleaseLock { public synchronized void testLockException(){ if (Thread.currentThread().getName().equals("A")){ System.out.println("线程名字为:"+Thread.currentThread().getName()+" 开始运行时间:"+System.currentTimeMillis()); for(int i=0;i<200;i++){ while(i>20){ System.out.println("i="+i); Integer.parseInt("abcd"); } } }else{ System.out.println("B线程运行时间:"+System.currentTimeMillis()); } } }
public class ExceptionReleaseThread1 extends Thread { private ExceptionReleaseLock exceptionReleaseLock; public ExceptionReleaseThread1(ExceptionReleaseLock lock){ this.exceptionReleaseLock=lock; } @Override public void run() { exceptionReleaseLock.testLockException(); } }
public class ExceptionReleaseThread2 extends Thread { private ExceptionReleaseLock exceptionReleaseLock; public ExceptionReleaseThread2(ExceptionReleaseLock lock){ this.exceptionReleaseLock=lock; } @Override public void run() { exceptionReleaseLock.testLockException(); } }
测试:
/** * Created by Administrator on 2015/12/30 0030. * * 发生异常后,锁自动释放 */public class TestExceptionRelease { public static void main(String[] args){ ExceptionReleaseLock lock = new ExceptionReleaseLock(); ExceptionReleaseThread1 a = new ExceptionReleaseThread1(lock); ExceptionReleaseThread2 b = new ExceptionReleaseThread2(lock); a.setName("A"); b.setName("B"); a.start(); b.start(); } }
输出结果:
A线程发生异常后释放了锁,线程B正常执行。
4. 同步不具备继承性.
public class Cat extends Animal { @Override public void eat() { System.out.println("noSynchronized ...."); } }
须要在方法前加上synchronized同步。
2、synchronized 同步语句块
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为
synchronized ,因为在线程的整个生命期内它一直在运行,所以将致使它对本类任何 synchronized 方法的调用都永远不会成功。固然咱们可
以经过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,可是 Java 为咱们提供
了更好的解决办法,那就是 synchronized 块。
2. synchronized 块:经过 synchronized关键字来声明synchronized 块。语法以下:
synchronized(syncObject) {
//容许访问控制的代码
}
synchronized 块是这样一个代码块,其中的代码必须得到对象 syncObject (如前所述,能够是类实例或类)的锁方能执行,具体机
制同前所述。因为能够针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
对synchronized(this)的一些理解
1、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程获得执行。另外一个线
程必须等待当前线程执行完这个代码块之后才能执行该代码块。
2、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另外一个线程仍然能够访问该object中的非synchronized
(this)同步代码块。
3、尤为关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其余线程对object中全部其它synchronized(this)
同步代码块的访问将被阻塞。
4、第三个例子一样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就得到了这个
object的对象锁。结果,其它线程对该object对象全部同步代码部分的访问都被暂时阻塞。
5、以上规则对其它对象锁一样适用
package com.xwolf.java.thread.ch2;/** * Created by Administrator on 2016/1/4 0004. * * synchronized 代码块 一样是对象加锁 */public class SyncBlock { public synchronized void a(){ System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" begin&&&&&&&&&&&&&&&"); try { Thread.sleep(2000); System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" end$$$$$$$$$$$$$$$$$$$$$$"); } catch (InterruptedException e) { e.printStackTrace(); } } public void b(){ try { synchronized (this){ System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" begin&&&&&&&&&&&&&&&"); Thread.sleep(2000); System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" end$$$$$$$$$$$$$$$$$$$$$$"); } } catch (InterruptedException e) { e.printStackTrace(); } } }
public class SyncThreadA extends Thread { private SyncBlock block; SyncThreadA(SyncBlock block){ this.block=block; } @Override public void run() { block.a(); } }
public class SyncThreadB extends Thread { private SyncBlock block; SyncThreadB(SyncBlock block){ this.block=block; } @Override public void run() { block.b(); } }
public class SyncThreadTest { public static void main(String[] args){ SyncBlock block =new SyncBlock(); SyncThreadA a = new SyncThreadA(block); a.setName("A"); SyncThreadB b = new SyncThreadB(block); b.setName("B"); a.start(); b.start(); } }
3、static synchronized 和非static synchronized 方法
package com.xwolf.java.thread.ch2;/** * Created by Administrator on 2016/1/4 0004. * * 验证static方法和非static方法synchronized 区别 */public class StaticSync { public synchronized static void a(){ System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" begin&&&&&&&&&&&&&&&"); try { Thread.sleep(2000); System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" end$$$$$$$$$$$$$$$$$$$$$$"); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized static void b(){ System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" begin&&&&&&&&&&&&&&&"); try { Thread.sleep(2000); System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" end$$$$$$$$$$$$$$$$$$$$$$"); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void c(){ System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" begin&&&&&&&&&&&&&&&"); try { Thread.sleep(2000); System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+" end$$$$$$$$$$$$$$$$$$$$$$"); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.xwolf.java.thread.ch2;/** * Created by Administrator on 2016/1/4 0004. */public class StaticThreadA extends Thread{ private StaticSync staticSync; public StaticThreadA(StaticSync sync){ this.staticSync=sync; } @Override public void run() { staticSync.a(); } }
测试:
package com.xwolf.java.thread.ch2;/** * Created by Administrator on 2016/1/4 0004. */public class StaticSyncTest { public static void main(String[] args){ StaticSync sync = new StaticSync(); StaticThreadA a = new StaticThreadA(sync); a.setName("A"); StaticThreadB b = new StaticThreadB(sync); b.setName("B"); StaticThreadC c = new StaticThreadC(sync); c.setName("C"); a.start(); b.start(); c.start(); } }
当一个synchronized关键字修饰的方法同时又被static修饰,以前说过,非静态的同步方法会将对象上锁,可是静态方法不属于对象,而是属于类,它会将这个方法所在的类的Class对象上锁。一个类无论生成多少个对象,它们所对应的是同一个Class对象。