多线程

1. 同步

在多个线程并发访问共享数据时,保证共享数据在同一时刻只被一条(或是一些,当使用信号量的时候)线程使用。

  • 互斥同步(阻塞同步)【悲观锁】

    • 一种最常见最主要的并发正确性保证手段。
    • 悲观的并发策略
      无论共享的数据是否真的会出现竞争,都会进行加锁。共享资源每次只给一个线程使用,其它线程阻塞。
    • 主要问题是进行线程阻塞和唤醒所带来的性能开销。
  • 非阻塞同步【乐观锁】

    • 基于冲突检测的乐观并发策略

    • 不需要把线程阻塞挂起,无锁编程
      不会上锁,先进行操作,若没有其他线程争用共享数据,操作直接就成功了;如果共享数据的确被争用,产生了冲突,再进行其他的补偿措施。(最常用的补偿措施是不断重试,直到出现没有竞争的共享数据为止)

    • CAS(Compare and Swap,比较并交换)

2. 可重入锁(ReentrantLock、Synchronized)

  • 线程可以反复获得已拥有的锁。同一线程能够反复进入被它自己持有锁的同步块,不会出现自己把自己锁死。
  • 同一线程,外层方法获取锁后,内层方法会直接获取锁。
  • 如果持有锁的线程再次获得这个锁,锁关联的计数器值加1,每次释放锁时计数器的值减1,当计数器的值变为0,线程释放锁。

3. Synchronized

需要reference类型的参数来指明要锁定和解锁的对象。

  • 同步方法(使用synchronized修饰方法,一次只有一个线程可以使用这个方法)
    public synchronized void deposit(double amount)
    首先先给对象加锁,然后执行该方法,最后解锁。在解锁之前,另一个调用这个对象中方法的线程将被阻塞,直到获取锁。

    • 实例方法
      默认用代码所在的对象实例(this)【调用这个方法的对象】作为线程要持有的锁

    • 静态方法(类方法)
      默认用类型对应的Class对象作为线程要持有的锁。

  • 同步代码块(修饰需要进行同步的代码)
    synchronized(object){代码内容}

    • 不仅可对this对象加锁,而且可对任何对象加锁。
    • 可以选择只同步会发生同步问题的部分代码而不是整个方法,大大增强了程序的并发能力。

4. 显式锁(Lock, eg:ReentrantLock)

  • 给协调线程带来了更多的控制功能。一个锁是一个Lock接口的实例,定义了加锁和释放锁的方法。 可以通过调用Lock对象的newCondition()方法创建任意个数的绑定到该Lock实例的Condition对象,然后使用Condition对象的await()、signal()、signalAll()方法【条件】来实现线程间的相互通信。
  • 如果条件不适合,线程可以调用条件的await()方法使线程进入等待状态,并自动释放条件上的锁;这样其他线程就可以获取锁,一旦条件合适,就可以调用signal()或signalAll()来通知一个或所有等待这个条件的线程;获得通知后,线程重新获取锁并继续执行。

5. volatile

  • 被volatile修饰的变量对所有线程都具有可见性,即一个线程修改了这个变量的值,则其他线程可以立即得知这个新值。
  • 禁止指令重排序优化

实现:在变量赋值后多执行了一个操作,这个操作的作用相当于一个内存屏障,指令重排序时不能把后面的指令重排序到内存屏障之前的位置。同时会把本处理器的缓存写入内存,该写入动作也会引起别的处理器或别的内核无效化其缓存,需要重新从内存中读取新的值。

注意

  • volatile不能完全保证多线程并发的安全性。因为运算操作符并非原子操作,例如自增,当读取变量原来的值时,volatile可以保证该值的正确性,但可能在执行加1之前,已经又有另一个线程对变量进行了修改,该值变为无效。

6. park()、unpark()

参考1
参考2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值