方法锁形式: synchronized 修饰普通方法, 锁对象默认为 this ,也就是当前实例对象html
注意若是是 不一样 类锁的 不一样 的同步代码块。状况是有点特殊的,虽然也是有顺序了。java
package com.my.com.my.sysnc; /** * 同步锁测试 */ public class TestSynchronized implements Runnable{ static TestSynchronized instance=new TestSynchronized(); Object lock1=new Object(); Object lock2=new Object(); @Override public void run() { synchronized (lock1){ System.out.println("lock1对象锁代码块,线程名"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"结束lock1"); } synchronized (lock2){ System.out.println("lock2对象锁代码块,线程名"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"结束lock2"); } } public static void main(String[] args) { Thread thread1=new Thread(instance); Thread thread2=new Thread(instance); thread1.start(); thread2.start(); } } 结果 即 ,在run 方法里面, 当 第一个线程执行完 第一个同步代码块的时候,在开始执行第一个代码块的时候。 此时 第二个线程会去执行第一个同步代码块。 这样就有 点并行执行了 : lock1对象锁代码块,线程名Thread-0 Thread-0结束lock1 lock2对象锁代码块,线程名Thread-0 lock1对象锁代码块,线程名Thread-1 Thread-0结束lock2 Thread-1结束lock1 lock2对象锁代码块,线程名Thread-1 Thread-1结束lock2
类锁面试
若是是对象锁的话,那么不一样的对象对于不一样的线程来讲,就不会起到 锁的做用了。ide
若是是 类锁的话,那么就能够作到 不一样的对象对于不一样的线程 起到 锁的做用。函数
普通方法: 即非 静态方法测试
抛出异常的适合, LOCK 锁不会释放锁,而 synchronized 会释放锁this
面试题spa
1. 两个线程同时访问一个对象的同步方法:.net
可以锁住,同一个实例线程
2. 两个线程访问的是两个对象的同步方法。
由于不一样的实例全部并无锁的做用,锁对象不是同一个
3. 两个线程访问的是 synchronized 的静态方法
锁的是 class对象, 锁住
4. 同时访问同步方法与非同步方法
同步方法不能影响 非同步方法。
同步方法与非同步方法能够同时运行
5. 访问同一个对象的不一样的普通同步方法
方法不能同时的运行。 锁的是同一个对象。受锁的影响
例子:
package com.my.com.my.sysnc; /** * 同步锁测试 */ public class TestSynchronized implements Runnable{ static TestSynchronized instance=new TestSynchronized(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ test1(); }else{ test2(); } } public synchronized void test1(){ System.out.println("对象锁代码块"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"结束线程"); } public synchronized void test2(){ System.out.println("对象锁代码块"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"结束线程"); } public static void main(String[] args) { Thread thread1=new Thread(instance); Thread thread2=new Thread(instance); thread1.start(); thread2.start(); } } 锁的是同一个实例,竞争的是同一个实例,因此受到影响 可能的结果 : 对象锁代码块Thread-1 Thread-1结束线程 对象锁代码块Thread-0 Thread-0结束线程
6. 同时访问静态 synchronized 和 非静态 synchronized 方法
锁不其做用,两个方法均可以一块儿运行。
由于静态锁的是 class 对象,非静态的 锁的是 实例对象。是不同的
package com.my.com.my.sysnc; /** * 同步锁测试 */ public class TestSynchronized implements Runnable{ static TestSynchronized instance=new TestSynchronized(); Object lock1=new Object(); Object lock2=new Object(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ test1(); }else{ test2(); } } public static synchronized void test1(){ System.out.println("静态加锁方法1,,对象锁代码块"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"结束线程"); } public synchronized void test2(){ System.out.println("非静态加锁方法2,对象锁代码块"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"结束线程"); } public static void main(String[] args) { Thread thread1=new Thread(instance); Thread thread2=new Thread(instance); thread1.start(); thread2.start(); } } 结果: 非静态加锁方法2,对象锁代码块Thread-1 静态加锁方法1,,对象锁代码块Thread-0 Thread-0结束线程 Thread-1结束线程
7. 方法抛异常后,会释放锁
package com.my.com.my.sysnc; /** * 同步锁测试 */ public class TestSynchronized implements Runnable{ static TestSynchronized instance=new TestSynchronized(); Object lock1=new Object(); Object lock2=new Object(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ test1(); }else{ test2(); } } public synchronized void test1(){ System.out.println("对象锁代码块"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } // 抛出异常后,释放锁,由JVM处理释放锁 throw new RuntimeException("抛出异常"); } public synchronized void test2(){ System.out.println("对象锁代码块"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"结束线程"); } public static void main(String[] args) { Thread thread1=new Thread(instance); Thread thread2=new Thread(instance); thread1.start(); thread2.start(); } } 结果: 对象锁代码块Thread-0 对象锁代码块Thread-1 Exception in thread "Thread-0" java.lang.RuntimeException: 抛出异常 at com.my.com.my.sysnc.TestSynchronized.test1(TestSynchronized.java:36) at com.my.com.my.sysnc.TestSynchronized.run(TestSynchronized.java:17) at java.lang.Thread.run(Thread.java:748) Thread-1结束线程
性质
1, 可重入:避免死锁,提高封装性
指的是 同一线程的外层函数得到锁以后,内层函数能够之间再次获取该锁
同一个方法可重入(好比递归调用),
不要求同一个方法也能够重入(好比同一个类中调用2个都是锁的方法) ,
可重入不要求 是同一个类中的(子类的 重写父类的加锁方法,并调用父类的加锁方法)
2,不可中断
3. 粒度
线程而非调用
原理
由于每个对象均可以做为 锁的部分。
因此每一个对象都有一个 内置锁。
线程在进入 synchronized 里面以前,会获取 synchronized 锁的对象的 内置锁。
当 执行完 synchronized 的 锁的代码后,会释放 对应的对象的 内置锁。
Java 对象的对象头有一个 字段用来讲明是否锁住了,因此 synchronized 做用就是 根据这个字段来 锁的
编译执行的时候 ,JVM 经过 Java的 指令 monitorenter 进入 锁(monitor计数器加1), monitorexit 退出 锁(monitor计数器减1)。
经过判断 monitor 数字 判断是否 是否能够获取锁 。
注意虽然synchronized 锁 等价于 Lock 锁。 可是他们其实仍是有所区别的。
lock 锁并非锁当前的 this 对象的。
package com.my.com.my.sysnc; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 同步锁测试 */ public class TestSynchronized implements Runnable{ static TestSynchronized instance=new TestSynchronized(); Lock lock=new ReentrantLock(); @Override public void run() { test1(); test2(); } public synchronized void test1(){ System.out.println("synchronized对象锁代码块"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("synchronized对象锁代码块结束"+Thread.currentThread().getName()); } public void test2(){ lock.lock(); try { System.out.println("lock对象锁代码块"+Thread.currentThread().getName()); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } }finally { //不释放锁 // lock.unlock(); } System.out.println(Thread.currentThread().getName()+"结束线程lock"); } public static void main(String[] args) { Thread thread1=new Thread(instance); Thread thread2=new Thread(instance); thread1.start(); thread2.start(); } } 线程0 在lock 执行后,并无释放lock 锁。 因此线程1 会在 lock 处等待。 由于 lock 不是 锁当前this 对象, 因此 线程1 能够执行 synchronized 锁方法 结果: synchronized对象锁代码块Thread-0 synchronized对象锁代码块结束Thread-0 lock对象锁代码块Thread-0 synchronized对象锁代码块Thread-1 Thread-0结束线程lock synchronized对象锁代码块结束Thread-1
注意点
1, 锁对象不能为空,做用域不宜过大,避免死锁
2, 如何选择 Lock 和 synchronized 关键字
21, 能使用 synchronized 优先使用,可以使代码更简化
22. 要使用 LOCK 的独有特性才考虑 lock
23. 总之就是 避免出错,追求稳定的
synchronized非公平锁,ReentrantLock能够设置是否公平锁
参考 https://www.cnblogs.com/twoheads/p/10150063.html
锁 | 优势 | 缺点 | 适用场景 |
---|---|---|---|
偏向锁 | 加锁和解锁不须要额外的消耗,与执行非同步方法仅存在纳秒级的差距 | 若是线程间存在竞争,会带来额外的锁撤销的消耗 | 适用于只有一个线程访问同步块的状况 |
轻量级锁 | 竞争的线程不会堵塞,提升了程序的响应速度 | 始终得不到锁的线程,使用自旋会消耗CPU | 追求响应时间,同步块执行速度很是块,只有两个线程竞争锁 |
重量级锁 | 线程竞争不使用自旋,不会消耗CPU | 线程堵塞,响应时间缓慢 | 追求吞吐量,同步块执行速度比较慢,竞争锁的线程大于2个 |
以上来自慕课网