第一节 进程和线程
1.1 进程(Process)
1 正在运行的程序,是一个程序的运行状态和资源占用(内存,CPU)的描述,通过进程ID区分。
2 进程是程序的一个动态过程,它指的是从代码加载到执行完毕的一个完成过程。
3 目前操作系统支持多进程多任务。
进程的特点:
a.独立性:不同的进程之间是独立的,相互之间资源不共享(举例:两个正在上课的教室有各自的财产,相互之间不共享)
b.动态性:进程在系统中不是静止不动的,而是在系统中一直活动的
c.并发性:多个进程可以在单个处理器上同时进行,且互不影响
1.2 线程(Thread)
1 线程就是一条执行路径。是进程的组成部分,一个进程可以有多个线程,每个线程去处理一个特定的子任务。
线程的特点:
线程的执行是抢占式的,多个线程在同一个进程中可以并发执行,其实就是CPU快速的在不同的线程之间切换,
也就是说,当前运行的线程在任何时候都有可能被挂起,以便另外一个线程可以运行。
1.3 进程和线程的关系以及区别
a.一个程序运行后至少有一个进程
b.一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义的
c.进程间不能共享资源,但线程之间可以
d.系统创建进程需要为该进程重新分配系统资源,而创建线程则容易的多,因此使用线程实现多任务并发比多进程的效率高
第二节 多线程的实现
多线程创建方式有三种:
1 继承Thread类
2 实现Runnable接口
3 实现Callable接口
2.1 继承Thread类
继承自Thread类,Thread类是所有线程类的父类,实现了对线程的抽取和封装
继承Thread类创建并启动多线程的步骤:
a.定义一个类,继承自Thread类,并重写该类的run方法,该run方法的方法体就代表了线程需要完成的任务,
因此,run方法的方法体被称为线程执行体
b.创建Thread子类的对象,即创建了子线程
c.用线程对象的start方法来启动该线程
注意:1 程序运行时会自动创建一个线程 ,这个线程叫主线程;可以通过主线程创建子线程。
2 启动线程使用start()方法,不要直接调用run()方法。
2.2 实现Runnable接口
实现Runnable接口创建并启动多线程的步骤:
a.定义一个Runnable接口的实现类,并重写该接口中的run方法,该run方法的方法体同样是该线程的线程执行体
b.创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象
c.调用线程对象的start方法来启动该线程
2.3 两种实现方式的比较
1 继承Thread类的方式
a.没有资源共享,编写简单
如果要访问当前线程,除了可以通过Thread.currentThread()方式之外,还可以使用getName()获取线程名字。
b.弊端:因为线程类已经继承了Thread类,则不能再继承其他类【单继承】
2 实现Runnable接口的方式
a.可以多个线程共享同一个资源,所以非常适合多个线程来处理同一份资源的情况
b.资源类实现了Runnable接口。如果资源类有多个操作,需要把功能提出来,单独实现Runnable接口。
c.弊端:编程稍微复杂,不直观,如果要访问当前线程,必须使用Thread.currentThread()
总结:实际上大多数的多线程应用都可以采用实现Runnable接口的方式来实现【推荐使用匿名内部类】
2.4 调用start()与run()方法的区别
当调用start()方法时将创建新的线程,并且执行run()方法里的代码,但是如果直接调用run()方法,不会创建新的线程。
2.5 线程的第三种创建方式:(了解)
使用Callable接口实现多线程
public class MyCallable implements Callable{
@Override
public Integer call() throws Exception {
int sum=0;
for(int i=1;i<=100;i++){
Thread.sleep(100);
sum+=i;
}
System.out.println("子线程执行了........."+Thread.currentThread().getName());
//System.out.println("子线程执行了........."+Thread.currentThread().getName());
return sum;
}
}
public static void main(String[] args) throws Exception{
//1创建Mycallable对象(可调用的)
MyCallable callable=new MyCallable();
//2创建一个任务实现Runable接口
FutureTask<Integer> task=new FutureTask<Integer>(callable);
//3创建线程对象
Thread thread=new Thread(task);
//4启动
thread.start();
//5获取返回值
Integer sum=task.get();//会等待子线程执行完毕,返回结果
System.out.println(sum);
}
第三节 线程的常用方法
3.1 设置获取线程的名称
设置
(1)使用构造方法
(2)setName(“xxx”);
获取
(1)super.getName();
(2)Thread.currentThread().getName();(推荐)
3.2 线程休眠
使得当前正在执行的线程休眠一段时间,释放时间片,导致线程进入阻塞状态
Thread.sleep(5000),5000的单位是毫秒,设置了sleep就相当于将当前线程挂起5s,这个操作跟线程的优先级无关,
当对应的时间到了之后,还会再继续执行
3.3 设置线程优先级
可以通过设置优先级来改变线程抢到时间片的概率,优先级高的线程获得较多的执行机会。
默认情况下,每个线程的优先级都与创建它的父线程具有相同的优先级,例如:main线程具有普通优先级,
则由main线程创建的子线程也有相同的普通优先级
注意:
优先级范围1~10,默认为5,对应的数值越大,说明优先级越高,这个方法的设置一定要在start之前
线程的优先级低并不意味着争抢不到时间片,只是抢到时间片的概率比较低而已
3.4 合并(加入)线程
在执行原来线程的过程中,如果遇到了合并线程,则优先执行合并进来的线程,
执行完合并进来的线程后,再回到原来的任务中,继续执行原来的线程。
特点:
a.线程合并,当前线程一定会释放cpu时间片,cpu会将时间片分给要Join的线程
b.哪个线程需要合并就在当前线程中,添加要合并的线程
c.join之前,一定要将线程处于准备状态start
3.5 后台线程
线程分为前台(用户)线程和后台(守护)线程。
后台线程:隐藏起来一直在默默运行的线程,直到进程结束,又被称为守护线程,JVM的垃圾回收线程就是典型的后台线程。
特征:如果所有的前台线程都死亡,后台线程会自动死亡。
前台线程:默认的线程都是前台线程,如果前台不执行完毕,程序不会退出。
3.6 线程让步
可以让当前正在执行的线程暂停,但它不会阻塞该线程,他只是将该线程转入就绪状态,
完全可能出现的情况是:当某个线程调用了yield方法暂停之后,
线程调度器又将其调度出来重新执行。
实际上,当某个线程调用了yield方法暂停之后,只有优先级与当前线程相同,
或者优先级比当前线程更高的就绪状态的线程才会获得执行的机会。
3.7 线程中断 interrupt()
程序在等待过程中,可以使用interrupt方法打断。
第四节 线程的生命周期
对于线程,当线程被创建,进入新生状态,调用start()方法 ,进入就绪状态(可执行状态),
如果抢到cpu,进入运行状态,运行过程中出现了阻塞,进入阻塞状态,如果程序正常结束进入死亡状态。
New(新生):线程被实例化,但是还没有开始执行
Runnable(就绪):没有抢到时间片
Running(运行):抢到了时间片,CPU开始处理这个线程中的任务
Blocked(阻塞): 线程在执行过程中遇到特殊情况,使得其他线程就可以获得执行的机会,
被阻塞的线程会等待合适的时机重新进入就绪状态
Dead(死亡):线程终止
a.run方法执行完成,线程正常结束【正常的死亡】
b.直接调用该线程的stop方法强制终止这个线程
State枚举表示线程的状态(六种)
NEW (新生)
RUNNABLE (可运行、运行)
BLOCKED (锁阻塞)
WAITING (等待)
TIMED_WAITING (限时等待)sleep(5000)
TERMINATED (终止死亡)
总结
1 进程:正在运行的程序,操作系统通过进程Id区分不同进程。
1. 正在运行的程序,是一个程序的运行状态和资源占用(内存,CPU)的描述,通过进程ID区分。
2. 进程是程序的一个动态过程,它指的是从代码加载到执行完毕的一个完成过程。
3. 目前操作系统支持多进程多任务。
2 线程:进程中的一条执行路径。
3 区别:
a.一个程序运行后至少有一个进程
b.一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义的
c.进程间不能共享资源,但线程之间可以
d.系统创建进程需要为该进程重新分配系统资源,而创建线程则容易的多,因此使用线程实现多任务并发比多进程的效率高
4 多线程的创建
(1)继承Thread ,重写run方法
(2)实现Runnable接口
(3)实现Callable接口 有返回值,可以抛出异常
5 线程的方法
线程对象.setName();
Thread.currentThread().getName();
Thread.sleep();
线程对象.priority();
线程对象.join();
线程对象.setDaemone();
Thread.yield();
线程对象.interrupt();
6声明周期
五个状态
新生—>就绪---->运行---->阻塞----->死亡