1、Semphore计数信号量

  • semphore是一个计数信号量
  • semphore包含了一组许可证
  • semphore有两个核心方法:
    • acquire():从信号量中获取一个许可,进入阻塞状态。
    • release():释放持有许可证的线程;
  • 然而,实际上并没有真实的许可证对象供线程使用,Semaphore只是对可用的数量进行管理维护
  • 总结:如果线程要访问一个资源就必须先获得信号量。如果信号量内部计数器大于0,信号量减1,然后允许共享这个资源;否则,如果信号量的计数器等于0,信号量将会把线程置入休眠直至计数器大于0.当信号量使用完时,必须释放

2、Synchronized同步隐式锁+wait()+notifyAll()

  • synchronized加锁解锁的过程是隐式的,用户不用手动操作,优点是操作简单
  • wait(): 使当前线程阻塞,前提是 必须先获得锁
  • notify():只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现
  • notifyAll ():会唤醒所有等待(对象的)线程
  • 在多线程中要测试某个条件的变化,使用if 还是while?

    • 要注意,notify唤醒沉睡的线程后,线程会接着上次的执行继续往下执行。所以在进行条件判断时候,可以先把 wait 语句忽略不计来进行考虑;显然,要确保程序一定要执行,并且要保证程序直到满足一定的条件再执行,要使用while进行等待,直到满足条件才继续往下执行

      3、ReentrantLock同步显式锁

  • ReentrantLock需要手动加锁和解锁,且解锁的操作尽量要放在finally代码块中,保证线程正确释放锁。

  • lock.lock():给当前线程上锁
  • 利用ReentrantLock可重入锁创建多个Condition分别控制多个线程
    1. - Condition c1=lock.newCondition();
    2. - c1.await()使当前线程进入阻塞状态
    3. - c1.signal()释放指定的线程
  • lock.unlock():进行解锁操作
  • 主要利用CAS+AQS队列来实现,CAS(Compare And Swap)比较并交换,CAS有3个操作数:内存值V、预期值A、要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

4、LockSupport类

  • LockSupport核心类的方法就两个:
    • part() :使当前线程阻塞,
    • unpark():唤醒指定线程进行调用
  • LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,可以把许可看成是一种(0,1)信号量(Semaphore),但与 Semaphore 不同的是,许可的累加上限是1。初始时,permit为0,当调用unpark()方法时,线程的permit加1,当调用park()方法时,如果permit为0,则调用线程进入阻塞状态。