一、并发与并行
并发:交替执行。指两个或多个事件在同一个时间段内发生。(一个人吃两个馒头)
并行:同时执行。指两个或多个事件在同一时刻发生。(两个人吃两个馒头)
二、线程与进程
进程:是指一个内存中运行的应用程序。一个应用程序可以同时运行多个进程。
线程:线程是进程中的一个执行单元,负者当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称为多线程程序。
线程调度:
分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
抢占式调度:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性 ),Java使用的是抢占式调度。
三、创建多线程程序的第一种方式:创建Thread类的子类
java.lang.Thread类:是描述线程的类。
构造方法:
public Thread(); // 分配一个新的线程对象。 public Thread(String name); // 分配一个指定名字的新的线程对象。 public Thread(Runnale target); // 分配一个带有指定目标的新的线程对象。 public Thread(Runnale target,String name); // 分配一个带有指定目标的新的线程对象并指定名字。
常用方法:
public String getName(); // 获取当前线程名称。 public void setName(); // 改变线程名称。 public void start(); // 导致此线程开始执行,java虚拟机调用此线程的run方法。 public void run(); // 此线程要执行的任务在此处定义代码。 public static void sleep(long millis); // 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。 public static Thread currentThread(); // 返回当前正在执行的线程对象。
四、创建多线程程序的第二种方式:实现Runnale接口
实现步骤:
1 创建一个Runnable接口的实现类
2. 在实现类中重写Runnable接口的run方法,设置线程任务
3. 创建一个Runnable接口的实现类对象
4. 创建Thread类对象,构造方法中传递Runnable接口的实现类对象
5. 调用Thread类中的start方法,开启新的线程执行run方法
五、线程安全
1. 线程安全与线程同步
使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。
为了保证每个线程能正常执行原子操作,java引入了线程同步机制。
三种方式完成同步操作:
1. 同步代码块
2.同步方法
3. 锁机制2. 同步代码块
同步代码块:synchronized 关键字可以用于方法中的某个区块,表示只对这个区块的资源实行互斥访问。
格式:synchronized(锁对象){ 可能会出现线程安全问题的代码(访问了共享数据的代码) }
注意:
1. 通过代码块中的锁对象,可以使用任意的对象
2. 但是必须保证多个线程使用的锁对象是同一个
3. 锁对象的作用:把同步代码块锁住,只让一个线程在同步代码块中执行3. 同步方法
使用步骤:
1. 把访问了共享数据的代码抽取出来,放到一个方法中
2. 在方法上添加synchronized修饰符格式:
修饰符 synchronized 返回值类型 方法名(参数列表){ 可能会出现线程安全问题的代码(访问了共享数据的代码) }
4. Lock锁
java.util.concurrent.locks.Lock接口
Lock 实现了提供比使用synchronized 方法和语句可获得更广泛的锁定操作。
Lock接口中的方法:
void lock(); // 获取锁 void unlock(): // 释放锁
java.util.concurrent.locks.ReentrankLock implements Lock接口
使用步骤:
1. 在成员位置创建一个ReenrantLock对象
2. 在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
3. 在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
六、线程状态
线程状态及导致状态发生条件
NEW(新建):线程刚被创建,但是并未启动。还没调用start方法。Runnable(可运行):线程可以在java虚拟机中运行的状态,可能正在运行自己的代码,也可能没有,这取决于操作系统处理器。
Blocked(锁阻塞):当一个线程试图获取第一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待):一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后市不能被自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能唤醒。
Timed Waiting(计时等待):同waiting状态,有几个方法有超时参数,调用他们进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep、Object.wait
Teminated:因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。
七、线程间通信
1. 概念:
多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。2. 为什么要处理线程间通信:
多个线程并发执行时,在默认情况下CPU是随机切换线程的,当我们需要多个线程共同完成一件任务,并且我们希望有规律的执行,那么线程之间需要一些协调通信。
3. 如何保证线程间通信有效利用资源:等待唤醒机制
八、线程池
1. 概念:
其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。2. 线程池(JDK1.5之后)使用:
java.util.concurrent.Executors:线程池的工厂类,用来生成线程池。
Exccutors类中的静态方法:
static ExecutorsService newFixedThreadPool (int nThreads) :创建一个可重用固定线程数的线程池
参数:int nThreads:创建线程池中包含的线程数量
返回值:ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程)。java.util.concurrent.ExecutorService:线程池接口
用来从线程池中获取线程,调用start方法,执行线程任务:submit(Runnable task)提交一个Runnable任务用于执行
关闭/销毁线程池的方法:void shutdown()3. 线程池的使用步骤:
1. 使用线程池的工厂类Executor里提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池。
2. 创建一个类,实现Runnable接口,重写run方法,设置线程任务。
3. 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法。
4. 调用ExecutorService中的方法shutdown销毁线程池(不建议执行)。