进程
运行中的任务通常对应一个进程。进程是处于运行中过程中的程序。并且具有一定的独立功能。进程是系统进行资源分配和调度的一个独立单位。
- 独立性:进程是系统中独立存在的实体,拥有自己独立的资源。
- 动态性:进程是一个正在系统中活动的指令集合,具有自己的生命周期和不同状态。
- 并发性 :多个进程可以在处理器中并发执行,不会互相影响。
线程
线程是进程的执行单元。一个进程可以拥有多个线程,每个线程互相独立。
一、线程的创建和启动
所有的线程对象都必须是Thread类或其子类的实例。
1、通过继承Thread类来创建
- 定义Thread类的子类,并重写run方法,run方法就是要执行的任务。
- 创建对象
- 调用线程的start方法启动线程。
2、通过Runnable接口创建
- 实现Runnable接口,重写接口的run方法
- 创建Runnable实现类的实例,作为Thread类的参数创建Thread类对象,
- 使用Thread对象调用start方法
3、使用Callable和Future创建线程
Callable接口使用call方法作为线程执行体,call方法可以有返回值,可以声名异常,使用Future接口来代表call方法的返回值。
FutureTask实现类Future接口也实现类Runnable接口,所有可以传入Thread创建线程。
二、线程的生命周期
1、五个状态
- 新建:分配内存阶段,new一个对象一样。
- 就绪:调用start方法的阶段,这个时候是就绪阶段,未必能够立刻进入运行,等待线程调用度的调度。不能重复调用start方法。
- 运行:线程活动cpu资源,开始执行线程体的时候,处于运行状态
- 阻塞:线程未必能一直处于运行状态,中断的时候进入阻塞状态,回到就绪状态等待调度。
- 死亡:线程体执行完,正常结束,或者抛出异常没有捕获会进入死亡状态,死亡状态的线程,不能重新调用start去启动,可用isAlive方法判断,新建、死亡的时候该方法返回false。
三、控制线程
1、join线程
join方法可以让一个线程等待另一个线程执行完毕后再执行。当在某个执行程序流中调用其他线程的join方法时,调用线程将被阻塞。
public static void main(String[] args) {
// 继承Thread的线程类
FirstThread ft = new FirstThread();
for (int i = 0; i < 100; i++){
System.out.println("当前线程:"+Thread.currentThread().getName()+i);
if (i == 20) {
try {
ft.start();
// 在main线程的执行体调用其他线程的join方法,main线程进入阻塞状态
ft.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2、后台线程
后台线程又称守护线程,它的任务就是为其他的线程提供服务。如果所有的前台线程都死亡,后台线程会自动死亡。
- setDaemon(true): 把线程指定为后台线程 ,在线程启动前设置,不然会报错。
- isDaemon(): 判断指定线程是否为后台线程。
垃圾回收线程就是后台线程。后台线程产出的线程默认也是后台线程。
3、线程睡眠sleep
sleep方法是是thread类的静态方法,可以让线程暂停一段时间,并进入阻塞状态。
- static void sleep(long millis):让线程暂停多少毫秒。
- static void sleep(long millis,int nanos):毫秒再加多少微妙,比较少用到。
4、线程让步yield
yield方法也叫线程让步,也是静态方法,它可以让当前正在执行的线程暂停,但不会阻塞线程,而是进入就绪状态重新排优先级。
sleep方法与yield方法的区别
- sleep方法暂停当前的线程后,会给其他线程执行机会,不会理会其他线程的优先级,但yield方法只会给优先级相同的或者优先级高的线程机会。
- sleep方法会将线程进入阻塞状态,而yield方法会让线程进入就绪状态
- sleep方法声明抛出InterruptedException异常,所以要么捕捉异常或者抛出,而yield没有声明异常抛出。
- sleep方法比yield方法有更好的移植性,一般不建议使用yield方法
5、改变线程的优先级
优先级高的线程获得较多的执行机会。
thread类提供了setPriority(int newPriority)、getPriority()方法来设置或获取线程的优先级,参数是一个整数,范围是1~10只间。thread类也提供了3个静态常量。
- MAX_PRIORITY :10
- MIN_PRIORITY:1
- NORM_PRIORITY:5
不同系统的优先级不同,所以用常量来设置,可以让程序有比较好的移植性。
四、线程同步
1、同步代码块
synchronized(obj){
}
obj是同步监视器,任何对象都可以作为同步监视器
2、同步方法
同步方法使用synchronized关键字来修饰方法。同步方法的监视器是this,对象本身。
3、释放锁的情况
- 当前线程的同步方法,同步代码块执行结束,当前线程即释放同步监视器。
- 遇到break、return终止了该代码块,该方法的继续执行,当前线程将会释放同步监视器。
- 出现了error或者exception,导致异常结束时,会释放。
- 使用wait()方法也会释放。
不会释放的情况:
- 调用sleep、yield方法来暂停的时候,当前线程不会释放监视器。
- 使用suspend方法也不会释放。尽量避免使用suspend()和resume()方法。
4、同步锁lock
java5后提供了Lock对象。lock是控制多个线程对共享资源进行访问的工具,每次只能有一个线程对lock对象进行加锁,线程开始访问共享资源前应先获得lock对象。
使用中,比较常用实现类ReentranLock。格式:
class x{
private final ReentrantLock lock = new ReentrantLock();
public void m(){
//加锁
lock.lock();
try{
//...
}
finally{
lock.unlock();
}
}
}
5、死锁
两个线程相互等待对方释放同步监视器时就会发生死锁。
五、线程的通信
使用Executors工厂类产生线程池。
- newCachedThreadPool():创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会缓存在线程池中。
- newFixedThreadPool(int nThreads):创建一个可重用的、具有固定线程数的线程池。
- newSingleThreadExecutor():创建只有一个线程的线程池。
- newScheduledThreadPool(int corePoolSize):创建具有指定线程数的线程池,它可以在指定延迟后执行线程任务。
- newSingleThreadScheduleExecutor():创建只有一个线程的线程池,可以在指定延迟后执行线程任务。