并发编程

2018/07/29 并发

操作系统发展回顾

简单批处理系统->多道批处理系统->分时处理

进程

进程的概念和特点

资源由操作系统分配(内存、I/O等)。

调度(操作系统为它管理的进程分配时间片),执行。

进程的状态

新建、就绪、运行、阻塞、退出。

  • 新建:分配进程ID、优先级。
  • 就绪:进程相关的数据创建完毕,放在就绪队列
  • 运行:进程被操作系统分配了时间片,处理器开始执行它。
  • 阻塞:I/O阻塞等。放入阻塞队列
  • 退出:进程执行完毕。

线程

线程的概念和特点

可以被调度和执行的单位通常被称作线程或者轻量级线程。

  • 一个进程至少对应一个线程。
  • 各个线程可以共享同一个进程中的各种资源。
  • 创建、切换、销毁线程的成本远低于原先进程的成本。
  • 线程通信比进程通信效率高。

线程的状态

创建、就绪、执行、阻塞、退出。

java线程

main线程

main线程是系统创建用来执行我们的程序。

任务

java中的任务被抽象成了一个Runnable接口

但是只有任务没什么用,需要创建一个线程去运行这个任务

Thread类

java中的Thread类来代表一个线程,我们需要关注它的这几种构造方法:

  • Thread(Runnable target, String name):在创建线程对象的时候传入需要执行的任务以及这个线程的名称。
  • Thread(Runnable target):只传入需要执行的任务,名称是系统自动生成的,或者可以在创建对象后再通过别的方法修改名称。
  • Thread(String name):只传入待创建线程的名称。
  • Thread():啥都不传,就是单纯构造一个线程对象而已。

执行任务,Thread类的start()方法负责开始执行一个线程,让一个线程运行起来有这么两种方法:

  • 创建Thread对象的时候指定需要执行的任务。
  • 通过继承Thread类并覆盖run方法。

这两种执行任务的方法说不上谁好谁坏,但是使用继承Thread类并且覆盖run方法的方式把线程和任务给弄到了一块儿,不可分割了,也就是所谓的耦合了,所以我们平时更倾向于使用任务和线程分开处理的第1种执行任务的方式。当然,有时候为了演示的方便,也是会使用继承Thread类并且覆盖run方法的方式。

线程相关方法

Thread类提供了许多方法来方便我们获取线程的信息或者控制线程。

  • 获取线程ID。
  • 获取和设置线程优先级。
  • 休眠。
  • 获取当前正在执行的线程。
  • 守护线程。
  • 让出本次处理器时间片。
  • 等待另一线程执行结束。join()。

void join():等待该线程终止才能继续执行

void join(long millis):在指定毫秒数内等待该线程终止,如果到达了指定时间就继续之行了。

void join(long millis, int nanos):跟上边方法一个意思,只不过加了个纳秒限制。

原子性操作

线程风险

共享变量:堆内存共享

原子性操作

一个或某几个操作只能在一个线程执行完之后,另一个线程才能开始执行该操作,也就是说这些操作是不可分割的,线程不能在这些操作上交替执行。

我们需要保证操作的原子性。

从共享性解决

  • 尽量使用局部变量解决问题。
  • 使用ThreadLocal类解决问题。不同线程操作同一个ThreadLocal对象执行各种操作而不会影响其他线程里的值

从可变性解决

让某个变量在程序运行过程中不可变,把它使用final修饰。

加锁解决

加锁的语法:

synchronized (锁对象) {
    需要保持原子性的一系列代码
}

锁的重入:只要一个线程持有了某个锁,那么它就可以进入任何被这个锁保护的代码块。

对于成员方法来说,我们可以直接用this作为锁。对于静态方法来说,我们可以直接用Class对象作为锁(Class对象可以直接在任何地方访问)。

参考

java并发编程之背景知识

Search

    Table of Contents