多线程

  • 线程同步
    • synchronized关键字:性能开销大
      • 同步一个方法/代码块:使多个线程不能同时访问
      • 一个类中的syn修饰不同的方法,不能分别调用
      • 解决方法:DCL
    • 锁:更细粒度
    • 状态升级——类锁和对象锁
      • 一个线程访问syn方法时,其他线程无法访问syn方法
      • 一个线程访问syn方法时,其他线程可以访问非syn方法
      • 一个线程访问syn方法时,其他线程可以访问类锁修饰的同步方法
      • syn修饰非静态,syn(this),syn(非this)是对象锁(就是说同一个对象中的两个syn不能同时被访问)
      • syn修饰静态方法以及同步代码块的syn(类.class)是类锁
    • ReenTrantLock可重入锁线程可以进入任何一个它已经拥有的锁所同步着的代码块。
    • RL和syn的区别:
      • syn基于JVM,RL基于JDK
      • Syn优化后(jdk1.6后)性能和RL差不多
      • RL可以指定(非)公平锁
      • RL可以指定分组来唤醒线程,syn只能唤醒一个或者全部
      • RL可以中断等待锁的线程
    • 自旋锁:让没有得到锁的线程自己运行一段时间
    • 偏向锁:如果当前线程没有竞争则减少对锁的检测
    • 轻量级锁:00 locked,优化锁的获取方式,用CAS替换MarkWord锁标记指向该锁记录指针。如果成功,改锁标记00,如果失败,变成重量级锁10
    • 重量级锁:10 monitor,阻塞的线程被系统挂机,需要进行系统上下文的切换才能得到CPU执行
    • 死锁:一个线程永远的占有一个锁
  • 线程的实现——继承Thread类
    • 创建线程:
      • 继承Thread类,扩展线程(更简单,其实也要实现runnable)
      • 实现Runnable接口(防止类需要继承其他类)
      • 实现Callable接口(比起Runnable,可以有返回值)
        • FucureTask ft = new FutureTask(callable);
        • ft.get()
    • 重写run()方法,调用start()方法启动线程而不是用run(),因为run只是定义需要执行的任务,而不是创建新的线程。用run还是在当前进程的执行方法
    • 状态:创建,就绪,运行,阻塞,终止
    • sleep和wait的区别:
      • sleep是thread类的方法,wait是object类中定义的方法
      • Sleep不会释放锁,wait会放弃锁,进入等待池
      • sleep和wait都会暂停。但是,wait后要别的线程执行notify才能重新开始(进入等锁池)
    • sleep和yield的区别
      • yield不能控制时间,而且不是进入阻塞,是就绪状态,sleep阻塞
      • sleep不考虑优先级,yield只考虑>=的优先级
      • sleep跑出interrupted异常,yield没有
      • sleep更好移植。
    • 上下文切换:存储和恢复CPU状态,使线程从中断点恢复执行
    • Join()主线程等待子线程结束,可以加超时参数
    • 停止线程
      • run完成
      • stop不推荐
      • interrupt不回终止正在运行的线程,需要加入判断才可以完成
    • 暂停线程
      • Interrupt
    • 优先级setPriority
    • 守护线程Daemon(GC)不能把正在运行的线程.setDaemon
    • BlockingQueue:解决生产者消费者的问题、
      • .put(阻塞到有空闲), .take(阻塞到有内容)
  • 线程池/Executor
    • CachedThreadPool:一个任务新建一个线程(注意要控制任务的数量)
    • FixedThreadPool:线程数不能超过线程池最大值,这个很好(但是空闲的时候也会消耗资源)如果超过了,就把任务放入queue按顺序执行
    • SingleThreadPool:相当于大小为1的FixedThreadPool。如果多余1个,则按顺序执行
    • ScheduleThreadPool:支持定时及周期性任务执行。
  • Volatile&Atomic
    • Volatile:修改变量后放入内存,从内存中读取数据,确保读取最新数据
      • 问题:i++,++i这样子的不安全
    • Atomic:通过线程安全的方式进行+和-的原子操作
  • 内存模型
    • 硬件:处理器上寄存器进行读写的速度比内存快太多,因此加入了告诉缓存。另外,为了使处理器的运算单元能充分被利用,处理器会对代码进行乱序执行
    • Java:变量都在主内存中;线程的工作内存保存了该线程用到的变量(副本),以及对变量的所有操作,线程间不能互相访问工作内存,只能通过访问主内存来传递。
    • 内存间的交互——从主内存copy到工作内存,再同步回主内存
      • Lock——把主内存的变量标记为某线程独占
      • Unlock——释放锁定的主内存变量
      • Read——把主内存中的变量值传输到工作内存中,以便Load
      • Load——把变量放入工作内存的副本中
      • Use——把工作内存的变量值传递给执行引擎
      • Assign——把执行引擎的值赋值给工作内存的变量
      • Store——把工作内存的变量值传送给主内存
      • Write——把store的值放入主内存中
    • 特性:
      • 原子性
      • 可见性
      • 有序性
    • Happen-Before原则(先行发生)
  • 线程同步的方法总结:
    • Syn方法/代码块
    • Wait/notify
    • Volatile
    • ReentrantLock
    • 局部变量
    • BlockingQueue
  • 线程安全
    • 不可变——其外部的可见状态永远也不会改变
    • 线程兼容——不是线程安全的大多数会是线程兼容的
    • 线程对立——避免
    • 实现方法:
      • 互斥(方法)同步(目的):syn,RL
      • 非阻塞同步:CAS
      • 无同步方案
Table of Contents