Java进阶六-多线程
一 多线程
- 相关概念
-
进程 (Process): 进程是程序的基本执行实体。
- 进程是操作系统分配资源的基本单位。
- 每个进程都有自己的内存空间、代码段、数据段等。
- 进程之间相互独立,一个进程的崩溃不会影响其他进程。
- 进程是程序的基本执行实体。
-
线程 (Thread): 应用软件中相互独立,可以同时运行的功能
- 线程是进程中最小的执行单元。
- 一个进程可以包含一个或多个线程。
- 线程共享同一进程的内存空间和其他资源。
- 线程之间的切换开销比进程小,因此多线程更高效。
- 线程是操作系统能够进行运算调度的最小单位。他被包含在进程之中,是进程中的实际运作单位。
-
并发 (Concurrency): 在同一时刻,有多个指令在单个CPU上交替运行。
- 并发是指多个线程在一段时间内交替执行。
- 在单核CPU上,线程轮流使用CPU时间片。
- 在多核CPU上,多个线程可以真正并行执行。
-
并行 (Parallelism): 在同一时刻,有多个指令在多个CPU上同时执行
- 并行是指多个线程同时执行。
- 需要多核CPU才能实现真正的并行执行。
1 多线程的三种实现方式对比
多线程的第一种启动方式:继承Thread类
- 1自己定义一个类去继承Thread
- 2重写run方法
- 3创建子类对象,并启动线程
代码实现:
自定义类:
public class shu16_2 extends Thread {@Overridepublic void run() {//书写线程要执行的代码for (int i = 0; i < 100; i++) {System.out.println("安贤好帅"+getName());}}
}
实例化对象:
public class shu16_1 {public static void main(String[] args) {//实例化对象shu16_2 p1 = new shu16_2();shu16_2 p2 = new shu16_2();shu16_2 p3 = new shu16_2();//起名字p1.setName("线程一");p2.setName("线程二");p3.setName("线程三");//启动线程p1.start();p2.start();p3.start();}
}
执行结果:
多线程的第二种启动方式:实现Runnable接口
- 1 自己定义一个类实现Runable接口
- 2 重写里面的run方法
- 3 创建自己的类和对象
- 4 创建一个Thread类的对象,并开启线程
代码实现:
自定义类
public class shu16_3 implements Runnable {@Overridepublic void run() {//书写需要执行的代码for (int i = 0; i < 100; i++) {Thread thread = Thread.currentThread();System.out.println("安贤好帅啊!" + thread.getName());}}
}
实例化对象
public class shu16_4 {public static void main(String[] args) {//实例化对象shu16_3 p1 = new shu16_3();//创建线程对象Thread thread1 = new Thread(p1);Thread thread2 = new Thread(p1);//给线程设置名字thread1.setName("thread1");thread2.setName("thread2");//开启线程thread1.start();thread2.start();}
}
运行结果:
多线程的第三种启动方式:利用Callable和Future接口
- 1 创建一个自定义类实现Callable接口
- 2 重写call(有返回值,表示多线程要运行的结果)
- 3 实例化对象(表示多线程要执行的任务)
- 4 创建FutureTask的对象(作用管理多线程运行的结果)
- 5 创建Thread对象开启线程
代码实现:
自定义类
import java.util.concurrent.Callable;public class shu16_5 implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= 100; i++) {sum += i;}return sum;}
}
实例化对象
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class shu16_6 {public static void main(String[] args) throws ExecutionException, InterruptedException {//实例化自定义对象shu16_5 p1 = new shu16_5();//实例化FutureTask对象用于管理多线程运行的结果FutureTask<Integer> ft = new FutureTask<>(p1);//创建线程对象Thread thread1 = new Thread(ft);//开启线程thread1.start();//获取多线程运行的结果Integer num = ft.get();System.out.println(num);}
}
运行结果:
2 多线程的常见成员方法
1 线程优先级
自定义类
public class shu16_10 implements Runnable{@Overridepublic void run() {Thread thread = Thread.currentThread();for(int i=1;i<=100;i++){System.out.println("hello world"+thread.getName()+i);}}
}
相关方法的调用
public class shu16_11 {public static void main(String[] args) {shu16_10 p1 = new shu16_10();Thread t1 = new Thread(p1, "线程一");Thread t2 = new Thread(p1, "线程二");//优先级 getPriority()--默认5 等级1-10越大越高int num1 = t1.getPriority();int num2 = t2.getPriority();System.out.println(num1 + " " + num2);//获取main主线程的优先级(概率性)System.out.println(Thread.currentThread().getPriority());System.out.println(Thread.currentThread().getName());//给线程设置优先级t1.setPriority(1);t2.setPriority(10);//启动线程t1.start();t2.start();}
}
2 守护线程
代码实现
自定义类
public class shu16_12 extends Thread{public shu16_12() {}public shu16_12(String name) {super(name);}@Overridepublic void run() {for (int i = 1; i <= 10; i++) {System.out.println(getName()+"@"+i);}}
}
public class shu16_13 extends Thread{public shu16_13() {}public shu16_13(String name) {super(name);}@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(getName() + "@" + i);}}
}
方法的调用
public class shu16_14 {public static void main(String[] args) {//创建对应的线程对象shu16_12 t1 = new shu16_12("女神");shu16_13 t2 = new shu16_13("备胎");//设置守护线程(备胎线程)//当其他非守护线程执行结束,非守护线程没有执行下去的必要了,会在短时间内迅速停止(不是立刻)t2.setDaemon(true);//开启线程t1.start();t2.start();}
}
运行结果:
3 出让/礼让线程
代码实现:
自定义类
public class shu16_15 extends Thread {public shu16_15() {}public shu16_15(String name) {super(name);}@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(getName() + " @ " + i);//出让当前CPU的执行权-减少连续长时间执行同一个线程的概率(但也不是绝对的)Thread.yield();}}
}
方法调用:
public class shu16_14 {public static void main(String[] args) {//创建对应的线程对象shu16_15 t1 = new shu16_15("坦克");shu16_15 t2 = new shu16_15("飞机");//开启线程t1.start();t2.start();}
}
运行结果:
4 插入/插队线程
代码实现:
自定义类:
public class shu16_16 extends Thread {public shu16_16() {}public shu16_16(String name) {super(name);}@Overridepublic void run() {for (int i = 1; i <= 10; i++) {System.out.println(i + " " + getName());}}
}
方法调用:
public class shu16_17 {public static void main(String[] args) throws InterruptedException {//创建线程shu16_16 t1 = new shu16_16("土豆");// //开启土豆线程
// t1.start();
//
// //main线程
// for(int i =1; i<=100; i++){
// System.out.println(Thread.currentThread().getName()+" "+i);
// }//开启土豆线程t1.start();//线程t1插到当前线程之前(当前线程main)t1.join();//main线程for(int i =1; i<=10; i++){System.out.println(Thread.currentThread().getName()+" "+i);}}
}
运行结果:
3 线程的生命周期
Question:sleep方法会让线程睡眠,线程睡眠时间到了之后,会立马执行下面的代码吗?
- 当睡眠时间到期后,线程会从阻塞状态变为可运行状态(Runnable),但并不保证它会立即执行。
- 即使睡眠时间到期,线程也需要与其他可运行状态的线程竞争CPU资源。
- 因此,睡眠时间到期后,线程可能会立即执行,也可能需要等待一段时间才能得到CPU时间片。
- 操作系统的线程调度器决定哪个线程获得CPU时间片。