1、进程、线程 和 程序
-
程序: 是为了完成特定任务 用某种语言编写的一组指令的集合。指的一段静态。
-
进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程:有他自身的产生 存在 以及消亡的过程–生命周期
程序是静态的 进程是动态的。进程作为资源分配单位,系统在运行时会为每个进程分配不同的内存区域。
- 线程(thread) 进程进一步细化就是线程,是一个程序内部的一条执行路径。
若一个进程同一时间并行执行多个线程,就是支持多线程。
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器,线程的切换开销比较小
2、单核CPU和多核CPU
在java中 一个程序至少有三个线程组成, 主线程(main) 垃圾回收线程(gc) 异常处理线程。
并行和并发:
- 并行:多个CPU同时执行多个任务
- 并发: 一个CPU在同一时刻,同时处理多个任务。
3、使用多线程的优点:
-
提高了应用程序的响应。
-
提高了计算机CPU的利用率
-
改善程序结构。
4、何时需要多线程?
- 程序需要同时执行两个或多个任务
- 程序需要实现一些需要等待的任务的时候:如:用户输入,文件读写 网络操作等。
- 需要一些后台运行的程序时
5、实现多线程
5.1 继承 Thread类
//自定义了一个线程类
public class MyThread extends Thread{
@Override
public void run() {
for(int i = 0 ; i < 100 ;i++){
System.out.println(i);
}
}
}
public static void main(String[] args) {
// 创建了两个线程
Thread t1 = new MyThread();
Thread t2 = new MyThread();
// t1.run();//调用线程的run方法 不属于多线程
// t2.run();
// start() 导致此线程开始执行; Java虚拟机调用此线程的run方法。
t1.start();//启动线程
t2.start();
}
为什么要重写run方法?
因为run方法是用来封装被线程执行的代码的。
run方和start方法的区别?
run()封装了线程执行的代码,直接调用的,相当于普通方法调用。
start方法 启动线程,然后由jvm调用此线程的run方法
Thread t3 = new Thread(){
@Override
public void run() {
System.out.println("创建线程 ");
}
};
5.2 实现 Runnable 接口
Runnable
接口应由任何类实现,其实例将由线程执行。 该类必须定义一个无参数的方法,称为run
public class MyRannable implements Runnable{
@Override
public void run() {
for (int i= 0 ; i < 1000 ; i ++){
System.out.println(i);
}
}
}
public class MyRunnableTest {
public static void main(String[] args) {
Runnable r = new MyRannable();
Thread t1 = new Thread(r);//线程负责执行Runnable接口的方法
Thread t2 = new Thread(r);
//启动线程
t1.start();
t2.start();
}
}
// 另一种写法
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程创建");
}
}).start();
6、设置和获取线程的名称
Thread(String name)
分配一个新的Thread
对象。
void | setName(String name) 将此线程的名称更改为等于参数 name 。 |
---|---|
void | setName(String name) 将此线程的名称更改为等于参数 name 。 |
static Thread | currentThread() 返回对当前正在执行的线程对象的引用。 |
public class MyRunnableTest {
public static void main(String[] args) {
Runnable r = new MyRannable();
Thread t1 = new Thread(r,"线程001");//线程负责执行Runnable接口的方法
Thread t2 = new Thread(r);
t2.setName("线程002");
//Thread t3 = new Thread(new Runnable() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程创建");
}
},"线程003").start();//设置线程名称
//t3.start();
//启动线程
t1.start();
t2.start();
System.out.println(Thread.currentThread().getName());//获取系统当前正在执行的线程
}
}
public class MyRannable implements Runnable{
@Override
public void run() {
for (int i= 0 ; i < 1000 ; i ++){
System.out.println(Thread.currentThread().getName()+"------------->"+i);
}
}
}
7、线程的优先级
int | getPriority() 返回此线程的优先级。 |
---|---|
void | setPriority(int newPriority) 更改此线程的优先级。 |
- 每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行
线程的两种调度模型
分时调度: 所有线程轮流实现CPU的使用权,平均分配每个线程占有CPU的时间片
抢占式调度: 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个。优先级高的线程获得CPU执行权的概率更高。
java是的是抢占式的调度模型。
线程的默认优先级为5,线程的优先级的范围为1–10
8、线程控制
void | join() 等待这个线程死亡 |
---|---|
void | setDaemon(boolean on) 将此线程标记为 daemon线程或用户线程。 |
static void | sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 |
9、线程的分类
线程分为:守护线程 用户线程
- 守护线程和用户线程基本上是相同的,唯一区别就是判断JVM何时离开
- 守护线程是用来服务用户线程的。通过在start方法之前调用thread.setDaemon(true) 可以将一个用户线程变成守护线程
- java的垃圾回收 他是一个典型的守护线程
- 如果JVM中都是守护线程,JVM将退出(用户线程执行结束 守护线程无论是否结束 都将终止执行)
10、线程的生命周期
线程的状态通常分为5种状态:
新建(NEW
):当一个Thread类及其子类的对象被声明并创建时,此时的线程对象就处于新建状态
就绪 (WAITING
| TIMED_WAITING
): 处于新建状态的线程被start后,线程将进入CPU的执行队列等待获得CPU的执行权,此时的线程已经具备了运行的条件,只是还未获得CPU的执行权
运行(RUNNABLE
):当就绪的线程获得CPU的执行权 ,处于运行状态
阻塞(BLOCKED
):在某种特殊的情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行。此时线程就进入阻塞状态
死亡(TERMINATED
):线程完成了他的全部工作或被线程被提前强制性的终止 或出现异常导致线程异常结束。
线程状态之间的转换