多线程与高并发之synchronized

一、进程与线程的概念

  • 进程
    在计算机里一个任务就是一个进程,进程是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。

  • 线程
    进程内部还需要同时执行多个子任务,这些子任务被称为线程。一个程序里不同的执行路径,就是一个线程

二、创建线程的三种方式

  1. 继承Thread类创建线程类
  2. 实现Runnerable接口或者Callable接口
  3. 通过线程池进行创建

三、线程生命周期(状态迁移圈)

  • New
  • Runnable(Ready Running)
  • TimedWaiting
  • Waiting
  • Blocked
  • Terminated

四、锁

多个线程同时运行的时候,现场的调度是由操作系统决定的,程序本身无法决定。当多个线程同时读写一个变量的时候,就有可能出现问题,因此需要加锁。

1、锁的分类

  • 对象锁
    在 Java 中,每个对象都会有一个 monitor 对象,这个对象其实就是 Java 对象的锁,通常会被称为“内置锁”或“对象锁”。类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰。

  • 类锁
    在 Java 中,针对每个类也有一个锁,可以称为“类锁”,类锁实际上是通过对象锁实现的,即类的 Class 对象锁。每个类只有一个 Class 对象,所以每个类只有一个类锁。

2、synchronized

特点
  1. 多线程同时修改变量会造成逻辑错误。因此需要通过synchronized同步(加锁)

  2. 同步的本质就是给指定对象加锁,注意加锁对象必须是同一个实例,线程获取到锁对象后,才可以执行被锁住的代码块

  3. 对jvm定义的单个原子操作不需要同步(加锁)

  4. 使用synchronized对一个对象加锁不用担心异常发生,无论有无异常发生,结束时都会释放锁。(出现异常时会释放锁,本来其他没有机会执行的线程会获得代码执行权)

分类

synchronized 的用法可以从两个维度上面分类:

1. 根据修饰对象分类

synchronized 可以修饰方法和代码块

  • 修饰代码块

    • synchronized(this|object) {}

    • synchronized(类.class) {}

  • 修饰方法

    • 修饰非静态方法

    • 修饰静态方法

2. 根据获取的锁分类

  • 获取对象锁

    • synchronized(this|object) {}

    • 修饰非静态方法,获取对象锁

  • 获取类锁

    • synchronized(类.class) {} 对类的class对象进行加锁

    • 修饰静态方法,获取类锁

补充:获取对象锁的两种方式基本上是等价的,但是synchronized(this|object) {}的方式可以不用锁住方法的全部代码,哪些代码需要加锁,就把这些代码放到用synchronized修饰的代码块中。在方法上用synchronized修饰,整个方法中的代码全部加锁。

对象加锁原理:加锁对象头部的前两位,用以表示锁的类型和状态,用来判断当前对象是否已被锁定。

知识点
  • 同步和非同步方法可以同时调用,互不影响

  • 模拟银行账户,对业务写方法加锁,对业务读方法不加锁,可行吗?

    • 看业务情况,如果业务允许读到脏数据,就可以对写加锁,读不加锁

    • 如果需要强一致性,则都要加锁

  • synchronized获得的锁是可重入的

    • 一个同步方法可以调用当前对象的用另外一个同步方法。一个线程已经拥有某个对象的锁,当这个线程再次申请的时候仍然会得到该对象的锁

    • 如果锁不可重入,就会导致死锁。

      • 因为一个线程已经拥有了对象的锁,而这时拥有锁的线程又要调用同一对象的另一个加锁方法,如果线程获得的锁不可重入,那么,获得锁的线程就会一直等待线程释放自己所拥有的锁,而当前线程只有正常执行完才可以释放锁,因此出现死锁的情况

      • 常见场景:子类调用父类的方法,而父子类的方法都加锁时,如果锁不可重入,就会出现死锁。

      • 所以synchronized获得的锁必须是可重入的。

锁升级
  • synchronized 最初的实现方式是“阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态切换需要耗费处理器时间,如果同步代码块中内容过于简单,这种切换的时间可能比用户代码执行的时间还长”。

  • 这种方式就是 synchronized实现同步最初的方式,这也是当初开发者诟病的地方。JDK6以前synchronized直接采用了重量级锁。JDK6中为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”。

  • 所以目前锁状态一种有四种,级别由低到高依次是:无锁、偏向锁,轻量级锁(自旋锁),重量级锁,锁状态只能升级,不能降级。

  • 并不是说用自旋锁就一定比用重量级锁更好,要具体情况具体分析。那什么时候适合用自旋锁,什么情况下又适合用重量级锁呢?

    • 执行时间短(加锁代码),线程数少,用自旋锁
    • 执行时间长,线程数多,用系统锁

详见文章 https://www.cnblogs.com/lwh1019/p/13093581.html

加锁带来的问题

synchronized代码块无法并发执行,因此程序的性能会下降。而且加锁和解锁都需要消耗时间,会降低程序的执行效率。synchronized以牺牲性能来获取程序一致性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值