2021年7月22日
线程介绍
前置概念
-
程序(program)
是为完成特定任务、用某种语言编写的一组指令的集合。简单的说:就是我们写的代码
-
进程
- 进程是指程序中运行的程序,比如我们使用qq,就启动了一个进程,操作系统就会为该进程分配内存空间,当我们使用迅雷,又启动了一个进程,操作系统就会为迅雷分配新的内存空间。
- 进程是程序的一次执行过程,或者是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程
什么是线程
- 线程是由进程创建的,是进程的一个实体
- 一个进程可以拥有多个线程
线程相关概念
- 单线程:同一个时刻,只允许执行一个线程
- 多线程:同一个时刻,可以执行多个线程,比如:一个QQ进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个软件
- 并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核COU实现的多任务就是并发
- 并行:同一个时刻,多个任务同时执行。多核CPU可以实现并行。
线程基本使用1——继承Thread类
-
创建线程的两种方式
在java中使用线程有两种方法
- 继承Thread类,重写run方法
- 实现Runnable接口,重写run方法
-
案例:每隔一秒钟输出“喵喵喵”
public class ThreadUse { public static void main(String[] args) { Cat cat = new Cat(); // 启动线程 cat.start(); } } class Cat extends Thread { int times = 0; @Override public void run() { while (true) { System.out.println("喵喵喵"+times); times++; // ctrl+alt+t try { // 休眠1秒 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (times >= 80) { break; } } } }
多线程机制
package com.hspedu; public class ThreadUse { public static void main(String[] args) { Cat cat = new Cat(); // 启动线程 cat.start(); // 当main线程启动子线程后Thread-0后,程序不会阻塞在start()方法,而是继续执行 // 注意是start()不是run(),详见下文 System.out.println("主线程继续执行" + Thread.currentThread().getName()); for (int i = 0; i < 10; i++) { System.out.println("主线程 i = " + i); // 让主线程休眠1秒 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Cat extends Thread { int times = 0; @Override public void run() { while (true) { times++; System.out.println("喵喵喵" + times + " 线程名:" + Thread.currentThread().getName()); // ctrl+alt+t try { // 休眠1秒 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (times >= 80) { break; } } } } //执行结果: //主线程继续执行main //主线程 i = 0 //喵喵喵1 线程名:Thread-0 //主线程 i = 1 //喵喵喵2 线程名:Thread-0 //主线程 i = 2 //喵喵喵3 线程名:Thread-0 //主线程 i = 3 //喵喵喵4 线程名:Thread-0 //主线程 i = 4 //喵喵喵5 线程名:Thread-0 //主线程 i = 5 //喵喵喵6 线程名:Thread-0 //主线程 i = 6 //喵喵喵7 线程名:Thread-0 //主线程 i = 7 //喵喵喵8 线程名:Thread-0 //主线程 i = 8 //主线程 i = 9
-
主线程结束,如果有子线程在运行,并不代表整个进程、程序结束。
未命名文件 (2).jpg
run()方法只是一个普通的方法,没有开启线程
-
读源码:
// (1) public synchronized void start() { start0(); } // (2) // start0() 是本地方法,是JVM调用,底层是c/c++实现 // 真正实现多线程的效果,是start0(),而不是run() private native void start0();
start()方法调用start0()方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态,具体什么时候执行,取决于CPU,由CPU统一调度。涉及到具体的操作系统。
线程基本使用2——实现Runnable接口
public class RunnableUse { public static void main(String[] args) { Dog dog = new Dog(); // 这里不能调用start(),更不应该调用run() // 创建Thread对象,将实现了Runnable接口的放入 Thread thread = new Thread(dog); thread.start(); } } class Dog implements Runnable { int count = 0; @Override public void run() { while (true) { count++; System.out.println("汪汪汪" + count + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (count >= 80) { break; } } } }
Runnable 底层使用了设计模式[代理模式]=>代码模拟实现Runnable接口
线程基本使用3——多线程执行
public class MoreThreads { public static void main(String[] args) { Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); Thread th1 = new Thread(thread1); Thread th2 = new Thread(thread2); th1.start(); th2.start(); System.out.println("主线程完毕") } } class Thread1 implements Runnable { int count = 0; @Override public void run() { while (true) { count++; System.out.println("Hello " + count + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (count >= 20) { break; } } } } class Thread2 implements Runnable { int count = 0; @Override public void run() { while (true) { count++; System.out.println("World " + count + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (count >= 25) { break; } } } }
线程如何理解
- 主线程创建不同线程,来执行不同的任务
- 不同线程是否结束互不关联
继承Thread和实现Runnable的区别
- 从JAVA设计上来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口start()->start0()
- 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单线程的限制。推荐使用Runnable。