synchronized锁
锁的理解业务案例synchronized可重入synchronized方法异常理论知识
synchronized锁 锁的理解在下面代码中锁的是o,锁的底层。任何人执行都得先拿到o这把锁,锁定是锁定的某个对象
synchronized是具备原子性的
private final Object o = new Object(); public void m() { synchronized (o) { count--; System.out.println(Thread.currentThread().getName() + "count=" + count); } }
以下代码中m_01和m_02以及m_03锁定是等值的
public void m_01() { synchronized (this) {//m_01和m_02是等值的 count--; System.out.println(Thread.currentThread().getName() + "count=" + count); } } public synchronized void m_02() {//锁的是T01.class对象 count--; System.out.println(Thread.currentThread().getName() + "count=" + count); } public void m_03() {//锁的是T01.class对象 synchronized (T01.class) { count--; System.out.println(Thread.currentThread().getName() + "count=" + count); } }业务案例
设置值的时候采用synchronized,获取的时候不采用加锁的方式
//设置加锁 public synchronized void set(String name, BigDecimal balance) { this.name = name; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } this.balance = balance; } //获取不加锁 public BigDecimal getBalance(String name) { return this.balance; } //容易产生脏读 public static void main(String[] args) { T03 t03 = new T03(); new Thread(() -> t03.set("zhangsan", BigDecimal.valueOf(100))).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("第一次读取 zhangsan的金额为:" + t03.getBalance("zhangsan")); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("第二次读取 zhangsan的金额为:" + t03.getBalance("zhangsan")); }
一个具有注脚的文本。1
synchronized是一把可重入锁。简单理解:调用m1获取一把锁,调用m2的时候发现是同一个线程,同样有锁,锁就是可重入的。
如果不可重入的话,导致第二个线程也需要获取锁。特别是涉及到继承的时候,如果子类继承父类。子类获取锁调用父类加锁的方法。会导致一直死锁。
synchronized void m1() { System.out.println("m1 start"); m2(); System.out.println("m1 end"); } synchronized void m2() { System.out.println("m2 start"); } public static void main(String[] args) { new T04().m1(); }synchronized方法异常
如果程序出现异常,锁会被释放、要是不想释放、这里可以让catch、继续执行。
如果抛出异常。会导致其他线程访问到上一个线程的数据。
int count = 1; synchronized void m1() { //try { for (int i = 0; i < 10; i++) { count++; System.out.println("count数据=" + count); if (i == 9) { int j = count / 0; } } count = 1; //} catch (Exception e) { // count = 1; // e.printStackTrace(); //} } public static void main(String[] args) { T05 t05 = new T05(); new Thread(() -> { System.out.println("第一线程打印数据"); t05.m1(); }).start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { System.out.println("第二线程打印数据"); t05.m1(); }).start(); }理论知识
synchronized的底层实现
1.JDK早期、重量级,只要加锁都会去找 *** 作系统获取锁。
2.synchronized升级之后:markword 偏向锁(只有一个线程的时候,会偏向该线程)多个线程之后升级为自旋锁(旋10次之后)升级为重量锁去 *** 作系统获取锁
3.锁只能升级。不能降级
4.什么时候使用自旋锁,占用CPU,不访问 *** 作系统,加锁和解锁的动作下的
5.自旋锁用户态:不使用内核态。
6.执行时间短(加锁代码),线程数少,使用自旋锁
7.执行时间长、线程数多,用系统锁(Syn)
8.不能用String常量 Integer Long做synchronized因为会出现不同的方法使用该常量的。导致使用该常量就多次加锁,出现死锁等情况。
读取尽量不使用synchronized,毕竟使用加锁效率会降低10倍以上的。 ↩︎
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)