目录
多线程概述
什么是多线程?
是指从软件或者硬件上, 实现多个线程并发执行的技术
具有多线程能力的计算机, 应具有硬件支持, 而能够在同一时间执行多个线程任务, 提升性能
什么是并行?
在同一时刻, 有多个指令在"多个"CPU上同时执行
举例: 多个厨师(CPU)同时炒多个菜(指令)
什么是并发?
在同一时刻, 有多个指令在"单个"CPU上交替执行
举例: 一个厨师(CPU)同时抄多个菜(指令)
什么是进程?
是一款运行的软件
1. 独立性: 指一个能运行的基本单位, 同时也是系统分配资源和调度的独立单位
2. 动态性: 进程的本质是程序的一次执行过程, 动态产生,动态消亡的
3. 并发性: 任何进程都可以同时和其他进程并发执行
什么是线程?
是进程中单个顺序控制流(软件中的一个功能), 是一条执行路径
线程是属于进程的(一款软件中的一个功能)
单线程和多线程举例?
单线程: 一个进程只有一条执行路径, 称为单线程
多线程: 一个进程如果有多条执行路径, 称为多线程(迅雷,360)
多线程的实现方式1 - 继承Thread
实现1: 继承Thread类
1. MyThread继承Thread类
2. 重写run方法
3. 测试类创建对象
4. 启动线程
代码示例:
class MyThreadTest{
public static void main(String[] args) {
//3. 测试类创建对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
//4. 启动线程: 能看到交替执行的现象
t1.start();
t2.start();
//t1.run(); //普通的创建对象,通过对象调用方法,并没有开启线程
//t2.run(); //普通的创建对象,通过对象调用方法,并没有开启线程
}
}
//1. MyThread继承Thread类
public class MyThread extends Thread{
//2. 重写run方法
@Override
public void run() {
for (int i = 1; i <= 100 ; i++) {
System.out.println("线程执行:" + i);
}
}
}
多线程的实现方式2 - 实现Runnable
实现2: 实现Runnable接口
1. MyRunnable实现Runnable接口
2. 重写run方法
3. 测试类创建MyRunnable对象
4. 测试类创建Thread对象,将MyRunnable对象作为参数传递
5. 启动线程: 能看到交替执行的现象
代码示例:
class TestMyRunnable{
public static void main(String[] args) {
//3. 测试类创建MyRunnable对象
MyRunnable mr = new MyRunnable();
//4.测试类创建Thread对象,将MyRunnable对象作为参数传递
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
//5. 启动线程: 能看到交替执行的现象
t1.start();
t2.start();
}
}
//1. MyRunnable实现Runnable接口
public class MyRunnable implements Runnable{
//2. 重写run方法
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("线程启动:" + i);
}
}
}
多线程的实现方式3 - 实现Callable和Future
实现3: 实现Callable接口
1. 定义MyCallable类实现Callable接口
2. 重写call方法 -> 需要返回线程执行完毕后的结果
3. 测试类创建MyCallable对象
4. 测试类创建Future的实现类对象FutureTask, 将MyCallable对象作为参数传递
5. 测试类创建Thread对象, 将FutureTask对象作为参数传递
6. 启动线程: 能看到交替执行的现象
注意:
如果线程没有执行完毕就调用了get, 那么就会卡住死等!
FutureTask的泛型和返回值类型匹配即可!
代码示例:
class MyCallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//3. 测试类创建MyCallable对象
MyCallable mc = new MyCallable();
//4. 测试类创建Future的实现类对象FutureTask, 将MyCallable对象作为参数传递
// 可以获取执行后的结果 -> get方法
FutureTask<String> ft = new FutureTask<>(mc);
// System.out.println(ft.get()); // 注意: 如果线程没有执行完毕就调用了get, 那么就会卡住死等!
//5. 测试类创建Thread对象, 将FutureTask对象作为参数传递
Thread t1 = new Thread(ft);
//6. 启动线程: 能看到交替执行的现象
t1.start();
//7.通过FutureTask对象的get方法, 获取执行结果 -> 注意: 如果线程没有执行完毕就调用了get, 那么就会卡住死等!
System.out.println(ft.get());
}
}
//1. 定义MyCallable实现Callable接口
public class MyCallable implements Callable {
//2. 重写call方法 -> 需要返回线程执行完毕后的结果
@Override
public String call() throws Exception {
for (int i = 1; i <= 100; i++) {
System.out.println("第" + i + "次买彩票");
}
return "买彩票终于中了500万!";
}
}
多线程的实现方式 - 两个小问题
两个小问题:
1. 为什么要重写run方法?
因为run方法是用来封装被线程执行的代码, 程序运行会执行run中的逻辑
2. run方法和start方法的区别?
如果执行run, 仅仅是普通的对象名调用方法执行, 并没有开启线程
查看start源码
public synchronized void start() {
...
//调用了start0
start0();
}
// 被native修饰的本地方法, 和本地操作系统交互, 开启线程的方法
private native void start0();
三种实现方式的对比
三种实现方式的对比:
实现Runnable和Callable接口
优点: 扩展性强, 实现接口的同时可以继承其他类
缺点: 变的复杂, 不能直接用Thread类中的方法(没有继承关系)
继承Thread类
优点: 编程简单, 可以直接用Thread类中的方法
缺点: 可扩展性差, 因为java只支持单继承
Thread方法 - 设置和获取名字
线程的默认的名字?
Thread-编号从0开始
查看Thread空参构造源码,能看到name格式
public Thread() {
//第三个参数就是name
this(null, null, "Thread-" + nextThreadNum(), 0);
}
private static synchronized int nextThreadNum() {
//让成员变量threadInitNumber每次自增
return threadInitNumber++;
}
//然后通过this.name = name;将计算出来的名字赋值给线程的成员变量name
获取线程名称:
String getName();
设置线程名称:
void setName(String name);
通过构造方法也可以设置线程名称()
代码示例:
class MyThreadTest{
public static void main(String[] args) {
//创建对象
//通过构造方法也可以设置线程名称()
MyThread t1 = new MyThread("吕布");
MyThread t2 = new MyThread("貂蝉");
//void setName(String name);
//t1.setName("张三");
//t2.setName("罗翔");
//启动线程
t1.start();
t2.start();
}
}
//继承Thread类实现多线程
public class MyThread extends Thread {
//要使用带参构造设置线程名称,需要提供构造方法
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(getName() + "线程执行:" + i);
}
}
}
Thread方法 - 获得线程对象
获取线程对象:
public static Thread Thread.currentThread(); 返回当前执行的线程对象
代码示例:
class MyRunnableTest {
public static void main(String[] args) {
//打印当前线程对象,打印名称
System.out.println(Thread.currentThread().getName()); //main
//创建MyRunnable对象
MyRunnable mr = new MyRunnable();
//创建Thread对象
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
//设置名称
t1.setName("吕布");
t2.setName("貂蝉");
//启动线程
t1.start();
t2.start();
}
}
//实现接口的方式,实现多线程
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
//由于没有继承Thread所以不能直接使用getName()方法
//System.out.println(getName() + "线程执行:" + i);
//public static Thread Thread.currentThread(); 返回当前执行的线程对象
System.out.println(Thread.currentThread().getName()+"线程执行:"+i);