一, 程序, 进程与线程的理解:
1, 程序 (program):
是为完成特定任务, 用某种语言编写的一组指令的集合.
即指一段静态的代码, 静态对象.
2, 进程 (process):
是程序的一次执行过程, 或是正在运行的一个程序.
是一个动态的过程: 有它自身的产生, 存在和消亡的过程, 即生命周期.
(1) 例如: 运行中的 QQ, 运行中的 MP3 播放器.
(2) 程序是静态的, 进程是动态的.
(3) 进程作为资源分配的单位, 系统在运行时会为每个进程分配不同的内存区域.
3, 线程 (thread):
进程可进一步细化为线程, 是一个程序内部的一条执行路径.
(1) 若一个进程同一时间并行执行多个线程, 就是支持多线程的.
(2) 线程作为调度和执行的单位, 每个线程拥有独立的运行栈和程序计数器 (pc), 线程切换的开销小.
(3) 一个进程中的多个线程共享相同的内存单元 / 内存地址空间:
它们从同一堆中分配对象, 可以访问相同的变量和对象.
这就使得线程间通信更简便, 高效. 但多个线程操作共享的系统资源可能就会带来安全的隐患.
二, 单核 CPU 与多核 CPU 的理解:
1, 单核 CPU:
其实是一种假的多线程, 因为在一个时间单元内, 也只能执行一个线程的任务.
例如: 虽然有多车道, 但是收费站只有一个工作人员在收费, 只有收了费才能通过,
那么 CPU 就好比收费人员. 如果有某个人不想交钱, 那么收费人员可以把他 "挂起".
但是因为 CPU 时间单元特别短, 因此感觉不出来.
2, 多核 CPU:
如果是多核的话, 才能更好的发挥多线程的效率. (现在的服务器都是多核的)
在一个 Java 应用程序 java.exe, 其实至少有三个线程: main() 主线程, gc(), 垃圾回收线程,
异常处理线程. 当然如果发生异常, 会影响主线程.
三, 并行与并发的理解:
1, 并行: 多个 CPU 同时执行多个任务. 比如多个人同时做不同的事.
2, 并发: 一个 CPU(采用时间片) 同时执行多个任务. 比如秒杀, 多个人做同一件事.
四, 多线程的理解:
1, 背景:
以单核 CPU 为例, 只使用单个线程先后完成多个任务 (调用多个方法),
肯定比用多个线程来完成用的时间更短, 为何仍需多线程呢?
2, 多线程程序的优点:
(1) 提高应用程序的响应. 对图形化界面更有意义, 可增强用户体验.
(2) 提高计算机系统 CPU 的利用率.
(3) 改善程序结构. 将既长又复杂的进程分为多个线程, 独立运行, 利于理解和修改.
3, 何时需要多线程:
(1) 程序需要同时执行两个或多个任务.
(2) 程序需要实现一些需要等待的任务时, 如用户输入, 文件读写操作, 网络操作, 搜索等.
(3) 需要一些后台运行的程序时.
五, 线程的创建和使用:
1,Thread 类的特性:
(1) 每个线程都是通过某个特定 Thread 对象的 run() 方法来完成操作的, 经常把 run() 方法的主体称为线程体
(2) 通过该 Thread 对象的 start() 方法来启动这个线程, 而非直接调用 run().
(3)Java 语言的 JVM 允许程序运行多个线程, 它通过 java.lang.Thread 类来体现.
2,Thread 类构造器:
(1)Thread(): 创建新的 Thread 对象.
(2)Thread(String threadname): 创建线程并指定线程实例名.
(3)Thread(Runnable target): 指定创建线程的目标对象, 它实现了 Runnable 接口中的 run 方法.
(4)Thread(Runnable target, String name): 创建新的 Thread 对象.
3,Thread 类成员方法:
●void start(): 启动线程, 并执行对象的 run() 方法.
●run(): 线程在被调度时执行的操作.
●String getName(): 返回线程的名称.
●void setName(String name): 设置该线程名称.
●static Thread currentThread(): 返回当前线程.
在 Thread 子类中就是 this, 通常用于主线程和 Runnable 实现类.
●static void yield(): 线程让步.
暂停当前正在执行的线程, 把执行机会让给优先级相同或更高的线程.
若队列中没有同优先级的线程, 忽略此方法.
●join(): 当某个程序执行流中调用其他线程的 join() 方法时, 调用线程将被阻塞,
直到 join() 方法加入的 join 线程执行完为止. 低优先级的线程也可以获得执行.
●static void sleep(long millis):(指定时间: 亳秒).
令当前活动线程在指定时间段内放弃对 CPU 控制, 使其他线程有机会被执行, 时间到后重排队.
抛出 InterruptedException 异常.
4,API 中创建线程的两种方式:
继承 Thread 类的方式, 实现 Runnable 接口的方式.
方式一: 继承 Thread 类
(1) 定义子类继承 Thread 类.
(2) 子类中重写 Thread 类中的 run 方法.
(3) 创建 Thread 子类对象, 即创建了线程对象.
(4) 调用线程对象 start 方法: 启动线程, 调用 run 方法.
- // 例子: 遍历 100 以内的所有的偶数
- //1. 创建一个继承于 Thread 类的子类
- class MyThread extends Thread {
- //2. 重写 Thread 类的 run()
- @Override
- public void run() {
- for (int i = 0; i <100; i++) {
- if(i % 2 == 0){
- System.out.println(Thread.currentThread().getName()
- + ":" + i);
- }
- }
- }
- }
- public class ThreadTest {
- public static void main(String[] args) {
- //3. 创建 Thread 类的子类的对象
- MyThread t1 = new MyThread();
- //4. 通过此对象调用 start(): 启动当前线程, 调用当前线程的 run()
- t1.start();
- // 问题一: 我们不能通过直接调用 run() 的方式启动线程.
- //t1.run();
- // 问题二: 再启动一个线程, 遍历 100 以内的偶数.
- // 不可以还让已经 start() 的线程去执行. 会报 IllegalThreadStateException
- //t1.start();
- // 我们需要重新创建一个线程的对象
- MyThread t2 = new MyThread();
- t2.start();
- // 如下操作仍然是在 main 线程中执行的.
- for (int i = 0; i < 100; i++) {
- if(i % 2 == 0){
- System.out.println(Thread.currentThread().getName()
- + ":" + i + "***********main()************");
- }
- }
- }
- }
注意事项:
1, 如果自己手动调用 run() 方法, 那么就只是普通方法, 没有启动多线程模式.
2,run() 方法由 JVM 调用, 什么时候调用, 执行的过程控制都有操作系统的 CPU 调度决定.
3, 想要启动多线程, 必须调用 start 方法.
4, 一个线程对象只能调用一次 star() 方法启动, 如果重复调用了,
则将抛出以上的异常 "llegalThreadStateException".
方式二: 实现 Runnable 接口
(1) 定义子类, 实现 Runnable 接口.
(2) 子类中重写 Runnable 接口中的 run 方法.
(3) 通过 Thread 类含参构造器创建线程对象.
(4) 将 Runnable 接口的子类对象作为实际参数传递给 Thread 类的构造器中.
(5) 调用 Thread 类的 start 方法: 开启线程, 调用 Runnable 子类接口的 run 方法.
- //1. 创建一个实现了 Runnable 接口的类
- class MThread implements Runnable{
- //2. 实现类去实现 Runnable 中的抽象方法: run()
- @Override
- public void run() {
- for (int i = 0; i < 100; i++) {
- if(i % 2 == 0){
- System.out.println(Thread.currentThread().getName()
- + ":" + i);
- }
- }
- }
- }
- public class ThreadTest1 {
- public static void main(String[] args) {
- //3. 创建实现类的对象
- MThread mThread = new MThread();
- //4. 将此对象作为参数传递到 Thread 类的构造器中, 创建 Thread 类的对象
- Thread t1 = new Thread(mThread);
- t1.setName("线程 1");
- //5. 通过 Thread 类的对象调用 start(): 启动线程,
- // 调用当前线程的 run()--> 调用了 Runnable 类型的 target 的 run()
- t1.start();
- // 再启动一个线程, 遍历 100 以内的偶数
- Thread t2 = new Thread(mThread);
- t2.setName("线程 2");
- t2.start();
- }
- }
比较创建线程的两种方式:
开发中: 优先选择实现 Runnable 接口的方式.
1, 实现的方式没有类的单继承性的局限性.
2., 实现的方式更适合来处理多个线程有共享数据的情况.
联系: public class Thread implements Runnable
相同点: 两种方式都需要重写 run(), 将线程要执行的逻辑声明在 run() 中.
来源: http://www.bubuko.com/infodetail-3383956.html