本章概述
** Java 中的多线程机制可以让一个程序中的多个程序段同时运行,同时运行的每一个程序段就是一个线程,这样多个同时运行的程序段既相互独立运行,又紧密相关。编程过程中采用多线程机制,可以使系统资源利用率更高,在一些情况下可以使程序设计更简单,程序中有耗时的程序段时,使用多线程可以使程序运行更加流畅。**
一. 进程.线程概述
1.1 进程和线程基本概念
下面是程序,进程和线程这几个概念的区别和联系
- 程序:是一段静态的代码,是人们解决问题的思维方式在计算机中的描述,是应用软件执行的蓝本,它是一个静态的概念,存放在外存上,没有运行的软件叫程序。
- 进程:是资源(CPU,内存等)分配的基本单位,是程序的一个运行例程,是用来描述程序的动态执行过程。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。
- 线程:是一条执行路径,是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。
1.2 进程
1. 进程的状态
- 新生状态(new):该状态表示一个进程刚刚被创建出来,还未完成初始化,不能被调度执行。在经过初始化之后,进程迁移至就绪状态。
- 就绪状态(ready):进程已经准备好,已分配到所需资源,只要分配到CPU就能够立即运行。在被调度器选择执行后,进程迁移至运行状态。
- 运行状态(running):该状态表示进程正在CPU上运行。当一个进程执行一段时间后,调度器可以选择中断它的执行并重新将其放回调度队列,它就迁移至就绪状态。当进程运行结束,它会迁移至终止状态。如果一个进程需要等待某些外部事件,它可以放弃CPU并迁移至阻塞状态。
- 阻塞状态(blocked):正在执行的进程由于某些事件(I/O请求,申请缓存区失败)而暂时无法运行,进程受到阻塞。在满足请求时进入就绪状态等待系统调用。
- 终止状态(terminated):进程结束,或出现错误,或被系统终止,进入终止状态。无法再执行。
** 2. 上下文切换**
为了使多个进程能够同时执行,OS进一步提出了**上下文切换(context switch)**机制,通过保存和恢复进程在运行过程中的状态,使进程可以暂停,切换和恢复,从而实现了CPU资源的共享。
1.3 线程
1. 多线程的地址空间布局
- 分离的内核栈和用户栈:由于每个线程的执行相对独立,进程为每个线程都准备了不同的栈(局部变量),供它们存放临时数据。在内核中,每个线程也有对应的内核栈。
- 共享的其他区域:进程除栈以为的其他区域由该进程的所有线程共享,包括堆(对象实例),方法区,运行时常量池(被加载的类)等。
二. 创建线程
线程只有创建之后才会存在,在Java中创建线程有两种方法:
- 通过继承 Thread 类
- 实现 Runnable 接口
在使用 Runnable 接口时,需要建立一个 Thread 实例。因此,无论是通过 Thread 类还是 Runnable 接口建立线程,都必须建立 Thread 类或它的子类的实例。
2.1 继承Thread 类创建线程
1. Thread 类的常用方法
- void run();线程运行时所执行的代码都在这个方法中,是 Runnable 接口声明的唯一方法
- void start(); 使该线程开始执行,Java虚拟机调用该线程的 run() 方法
- void interrupt(); 中断线程
- void join(); 等待该线程终止,它有多个重载方法
- static void yield(); 暂停当前正在执行的线程对象,并执行其他线程
2. 继承Thread类创建线程的步骤为:
- 创建一个类继承Thread类,重写run()方法,将所要完成的任务代码写进run()方法中
- 创建Thread类的子类的对象
- 调用该对象的start()方法,该start()方法表示先开启线程,然后调用run()方法
通过继承 Thread 类来实现一个线程类。在主线程执行时创建两个子线程,它们一起并发运行。
public class ThreadDemo extends Thread { private Thread t; private final String threadName; ThreadDemo(String name){ threadName = name; System.out.println("创建线程" + threadName); } public void run(){ System.out.println("运行线程" + threadName); try{ System.out.println("线程" + threadName + "休息一会"); Thread.sleep(10); } catch (InterruptedException e){ System.out.println("线程" + threadName + "中断."); } System.out.println("线程" + threadName + "结束."); } public void ready(){ System.out.println("启动线程" + threadName); this.start(); } } public class ThreadApp { public static void main(String[] args) { ThreadDemo t1 = new ThreadDemo("Thread-1"); t1.ready(); ThreadDemo t2 = new ThreadDemo("Thread-2"); t2.ready(); } }
2.2 通过 Runnable 接口创建线程
1. 实现Runnable接口创建线程的步骤为:
- 创建一个类并实现 Runnable 接口
- 重写 run() 方法,将所要完成的任务代码写进 run() 方法中
- 创建实现 Runnable 接口的类的对象,将该对象当做 Thread 类的构造方法中的参数传进去
- 使用 Thread 类的构造方法创建一个对象,并调用 start() 方法即可运行该线程
通过实现 Runnable 接口来实现一个线程类,在主线程中实例化这个子线程对象并启动,子线程执行时,会在给定的时间间隔不断显示当前时间。
public class TimePrinter implements Runnable{ public boolean stop = false; //线程是否停止 int pauseTime; //时钟跳变时间间隔 String name; public TimePrinter(int x,String n){ pauseTime = x; name = n; } public void run(){ while ((!stop)) { try { System.out.println(name + ":" + new Date(System.currentTimeMillis())); Thread.sleep(pauseTime); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class NewThread { public static void main(String[] args) { TimePrinter tp = new TimePrinter(1000,"当前日期时间"); Thread t = new Thread(tp); t.start(); System.out.println("按回车键终止!"); try{ System.in.read(); } catch (Exception e){ e.printStackTrace(); } tp.stop = true; } }
版权归原作者 Ombré_mi 所有, 如有侵权,请联系我们删除。