进程与线程
-
当一个程序进入内存运行时,就变成一个进程
-
进程就是在内存中运行的程序
-
进程的特点:
- 独立性:进程在内存中独立存在,拥有独立的资源,不能随意访问其他进程的地址空间
- 动态性:程序只是磁盘上的静态指令,而进程是活动的,有自己的生命周期和活动状态
- 并发性:多个进程可以在cpu中并发执行
-
并行vs并发
- 并行:多条指令在多个cpu上执行
- 并发:多条指令快速轮换执行,达到类似同时执行的效果
-
为什么现代操作系统可以同时执行多个任务?一边写代码、一边听音乐、还有各种后台程序管理计算机?
- 其实并没有同时运行,只是轮流执行,采用抢占式多任务
- 由于cpu执行速度非常快,用户感觉上就是轮流执行
-
什么是线程
- 线程是进程的执行单元,线程依赖于进程执行
- 多个线程共享父进程的内存资源
- 线程可以拥有自己的程序计数器、局部变量
- 线程的执行是抢占式的
-
多线程的应用
- 浏览器同时下载多个文件
- 服务器同时响应多个请求
- Java虚拟机存在一个超级线程用于资源回收
-
总结
- 操作系统可以执行多任务,每个任务就是进程
- 一个进程可以执行多任务,每个任务就是线程
线程的创建方法
- 线程的创建方法1:继承Thread类
public class MyThread extends Thread{
public static void main(String[] args) {
//创建对象,通过start方法启动线程
MyThread myThread = new MyThread();
myThread.start();
}
@Override
//重写run方法
public void run() {
System.out.println("启动线程");;
}
}
-
Thread常用方法
- 创建Thread的时候可以在参数中指定名字
- setName设置名字
- getName获取名字,默认为Thread-0、Thread-1……
-
线程的创建方法2:实现Runnable接口
//实现Runnable接口
public class MyThread implements Runnable{
public static void main(String[] args) {
//通过Thread类的有参构造器,传入实现了Runnable接口的类
Thread myThread = new Thread(new MyThread());
myThread.start();
}
@Override
//实现run方法
public void run() {
System.out.println("启动线程");;
}
}
可以简化为匿名类方式实现Runnable接口
public class MyThread{
public static void main(String[] args) {
//通过Thread类的有参构造器,传入Runnable接口
//用匿名类实现Runnable接口
Thread myThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程启动");
}
});
myThread.start();
}
}
由于Runnable是函数式接口,可以直接用Lambda表达式实现:
public class MyThread{
public static void main(String[] args) {
//通过Thread类的有参构造器,传入Runnable接口
//用Lambda表达式实现接口
Thread myThread = new Thread(() -> System.out.println("线程启动"));
myThread.start();
}
}
- 线程的创建方法3:实现callable接口
- 与Runnable的区别:
--可以有返回值,通过泛型指定
--call方法可以抛出异常
--先用futureTask类包装callable接口,再传入Thread构造器,通futureTask可调用方法获取call方法的返回值
- 与Runnable的区别:
public class MyThread implements Callable<String> {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//通过FutureTask包装Callable接口
FutureTask<String> futureTask = new FutureTask<>(new MyThread());
//通过Thread的构造器传入Callable接口
new Thread(futureTask).start();
//可获取返回值
System.out.println(futureTask.get());
}
@Override
public String call() throws Exception {
return "线程启动";
}
}
- 推荐使用Runnable和Callable接口
- 实现接口后,类还可以继承别的类
- 多个线程可以共享同一个Runnable或Callable对象,以及这些对象内的变量
线程的状态
状态 | 说明 |
---|---|
新建 | new创建线程后,还未调用start运行 |
就绪 | 1、调用start后,JVM为其创建方法调用栈和程序计数器,线程试图抢占cpu资源 2、阻塞解除后,线程试图抢占cpu资源 |
运行 | 抢占到cpu资源,开始执行run方法 |
阻塞 | 1、线程调用sleep方法 2、调用了一个阻塞式IO方法 3、执行到了wait方法,等待notify 4、试图获取同步监视器(synchronize),但该同步监视器被其他线程占有 |
死亡 | run方法执行完成 或者,线程抛出未捕获的异常 |

- 若干说明:
- 从阻塞状态恢复后只能回到就绪状态,不能直接运行
- 只能对新建状态的线程调用start方法,否则会抛出线程状态异常,因此不能对一个线程对象调用两次start
控制线程
-
join线程
- 调用join方法后,调用本线程的线程(例如main线程),会等本线程执行完再执行主线程
Thread thread = new Thread(new MyThread());
thread.start();
//join后,主线程会被阻塞,等待thread线程结束后,才重新执行主线程
thread.join();
//参数表示主程序的等待时间为1000ms,超过时间则不再等待
thread.join(1000);
- 后台线程、守护线程
- setDaemon(true)方法
- 必须在start之前设置
- 当其他线程都结束后,后台线程才死亡
- 线程睡眠sleep
- sleep后,线程进入阻塞状态,即时没有可运行的线程,也必须等待
网友评论