文章目录
  1. 1. 一、进程和线程
  2. 2. 二、Java内存模型
  3. 3. 三、并发编程需要考虑的问题
  4. 4. 四、volatile和synchronized
  5. 5. 五、线程状态改变

一、进程和线程

  • 进程对应的是程序,每个进程对应一定的地址空间,暂停时保存当前的状态,为进程切换提供了可能
  • 单核CPU的话任一时间只有一个进程在占用CPU
  • 针对进程子任务只能串行的问题,出现了线程的概念。每个线程对应一个子任务。(为什么不多进程?分配进程成本高,进程切换消耗大,进程间资源不共享)
  • 进程让操作系统的并发性成为可能,而线程让进程的内部并发成为可能。进程是操作系统进行资源分配的基本单位,而线程是操作系统进行调度的基本单位。线程共享进程的资源

二、Java内存模型

  • 线程间共享变量存储在主内存中
  • 每个线程都有私有的本地工作内存,本地内存中存储了共享变量的副本,线程对变量的读写操作都在本地进行。

三、并发编程需要考虑的问题

  • 共享性

    • 每个线程操作的始终是本地内存中的变量
    • Servlet以单实例多线程的方式工作。只要Servlet中的代码只使用局部变量,Servlet就不会导致同步问题。
    • Spring MVC的控制器也是这么做的,从请求中获得的对象都是以方法的参数传入而不是作为类的成员。
    • Struts2的做法正好相反,因此Struts2中作为控制器的Action类都是每个请求对应一个实例
  • 互斥性

    • 加锁(共享锁、排他锁)
    • 不变模式,用final修饰。Java中的Spring就是具有不变形的代表,所以是线程安全的
  • 原子性

    • volatile无法保证原子性
  • 可见性

    • volatile修饰的变量,更改后会立刻刷新到主内存中,其他线程也会进行同步
  • 有序性

    • 编译级别的重排序,比如编译器的优化
    • 指令级重排序,比如CPU指令执行的重排序
    • 内存系统的重排序,比如缓存和读写缓冲区导致的重排序

四、volatile和synchronized

  • 实现原理:

    • volatile:lock前缀指令实现内存屏障,防止指令重排,并且把指令直接更新到主存中
    • synchronized:在JVM层次实现,使用monitor(每个对象都有一个监视器锁)来实现同步,其中同步代码块采用monitorenter、monitorexit指令显式实现,而同步方法则使用ACC_SYNCHRONIZED标记符隐式实现
  • 修饰对象:

    • volatile:修饰变量
    • synchronized:修饰成员方法(锁该对象),修饰静态方法(锁该类的.Class对象),修饰代码块(可以指定需要哪个对象的锁)。

五、线程状态改变

  • Object的方法:wait/notify/notifyAll
  • Thread的方法:sleep/join/yield
    • wait:释放锁,进入等待池
    • join:使主线程等待子线程执行完成后再执行,换句话说就是将线程的并行执行变为串行执行
    • yield:线程让出CPU,回到就绪状态
文章目录
  1. 1. 一、进程和线程
  2. 2. 二、Java内存模型
  3. 3. 三、并发编程需要考虑的问题
  4. 4. 四、volatile和synchronized
  5. 5. 五、线程状态改变