目录
认识进程和线程
- 进程指的一段正在运行的程序,一个程序运行中可以执行多个任务,任务称之为线程
- 进程是程序执行过程中资源分配和管理的最小单位 线程是cpu执行的最小单位
- 进程拥有自己的独立的地址空间,每启动一个线程,系统就会分配地址空间
- 进程可以拥有多个线程,各个线程之间共享程序的内存空间
为什么出现线程
在多线程操作系统中,通常是在一个进程中包括多个线程,每个线程都是独立调度和分派的基本单位。资源由进程拥有,线程不拥有资源。同一个进程之间的线程切换不会导致进程的切换,只有不同进程间的线程切换才会导致进程切换。而且线程的切换则仅需保存和设置少量寄存器内容,不会同进程切换需求创建和销毁进程控制块等,所以非常迅速,所以其十分适合高并发环境。
多线程和多进程的区别
区别 | 多进程 | 多线程 |
---|---|---|
地址空间 | 独立的地址空间 | 共享本进程的地址空间 |
资源拥有 | 资源是独立的 | 共享本进程的资源 |
导管 | 进程崩溃后,不会对其他进程产生影响 | 线程崩溃会影响整个进程 |
执行过程 | 每个独立的进程程有一个程序运行的入口、顺序执行序列和程序入口 | 线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制 |
单位 | 进程是资源分配的最小单位 | 线程是cpu调度的最小单位 |
创建/销毁开销 | 大 | 小 |
使用 | 追求系统更加稳定,选择进程 | 追求速度,频繁创建和销毁,选择线程 |
- 线程之间的通信相比于进程之间的通信更有效 更容易
- 多进程要比多线程健壮
- 线程是轻量级的进程
线程的创建
1. 继承Thread类,重写run()方法
class MyThread extends Thread {
@Override
public void run(){
//线程执行体
while (true){
System.out.println("吃饭");
}
}
}
class MyThread1 extends Thread {
@Override
public void run(){
//线程执行体
while (true){
System.out.println("玩手机");
}
}
}
public class TestThread {
public static void main(String[] args) {
//创建线程对象
Thread thread = new MyThread();
Thread thread1 = new MyThread1();
//启动线程
thread.start();
thread1.start();
}
}
2. 实现Runnable接口,重写run()方法
class MyThread implements Runnable {
@Override
public void run(){
//线程执行体
while (true){
System.out.println("吃饭");
}
}
}
class MyThread1 implements Runnable{
@Override
public void run(){
//线程执行体
while (true){
System.out.println("玩手机");
}
}
}
public class TestThread {
public static void main(String[] args) {
//创建线程对象
Thread thread = new Thread(new MyThread());
Thread thread1 = new Thread(new MyThread1());
//启动线程
thread.start();
thread1.start();
}
}
3. 实现Callable接口,重写call()方法
Callable接口存在Executor框架中类,相比于Runnable更加强大
a.Callable可以在任务执行结束之后提供一个返回值
b.call方法可以抛出异常
c.运行Callable任务可以拿到一个Future对象,Future提供get方法
d.拿到返回值(异步)
- 通过Callable和FutureTask创建线程:
a.创建Callable接口的实现类,重写call方法
b.创建Callable实现类的实例,使用FutureTask包装该实例
c.将FutureTask实例作为参数创建线程对象
d.启动该线程
e.调用FutureTask的get方法获取子线程的执行结果
public class TestThread {
public static void main(String[] args) {
//2.创建Callable实现类的实例,使用FutureTask包装该实例
Callable<Integer> callableTask = new MyCallable();
FutureTask<Integer> task = new FutureTask<>(callableTask);
//3.将FutureTask实例作为参数创建线程对象
Thread thread = new Thread(task);
//4.启动该线程
thread.start();
//5.调用FutureTask的get方法获取子线程的执行结果
try {
Integer integer = task.get();
System.out.println("result: "+integer);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
Runnable接口和Callable接口的区别
区别 | Runnable | Callable |
---|---|---|
函数返回值 | run()无返回值 | call()有返回值,支持泛型 |
异常处理 | run()只能抛出运行时异常,且无法捕获处理 | call()允许抛出异常,可以获取异常信息 |
小练习:12306卖车票
public class Web12306 implements Runnable{
//票数
private int ticketNums = 99;
@Override
public void run(){
while (ticketNums > 0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ticketNums--);
}
}
public static void main(String[] args) {
//一份资源
Web12306 web12306 = new Web12306();
System.out.println(Thread.currentThread().getName());
//多个代理
new Thread(web12306,"码农").start();
new Thread(web12306,"码畜").start();
new Thread(web12306,"蚂蟥").start();
}
}