day07【多线程】
今日内容介绍
进程概念
线程概念
线程的创建方式
线程名字设置获取
线程安全问题引发
同步代码块
同步方法
死锁
第一章 多线程的概念【重点】
1.1 并行和并发
并行: 同一时刻,多个程序同时执行
并发: 同一时间段,多个程序交替执行
1.2 进程和线程的介绍
进程: 正在执行的一个软件/程序
每个进程:占用独立的内存空间
线程: 进程中的一个子程序,进程中的一个执行路径/执行单元
每个线程也有独立的内存空间
1.3 线程调度的原理
1.分时调度:
为每个线程分配相同的执行时间
2.抢占式调度:
CPU优先执行优先级别高的线程,多个相同优先级的线程,CPU就随机选择一个执行
1.4 主线程
一个可执行程序中
至少有一个线程,用于执行main方法中的代码,称为主线程/main线程
第二章 线程的创建-继承方式
2.1 创建线程的第一种方式
java.lang.Thread类: 代表线程类
线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
创建线程的第一种方式: 继承Thread类
1.定义类继承Thread类
2.Thread类的子类覆盖重写run方法,作用是指定线程要执行的任务
3.创建Thread类的子类对象
4.Thread类的子类对象调用start方法,开启线程
注意:
1.开启线程必须调用start方法,不能直接调用run方法
如果调用run方法,那么就是普通的方法调用,没有多线程的执行效果
API中start方法说明: 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
2.调用start方法,目的是以多线程的方式执行线程对象中的run方法
当调用start方法时,start方法瞬间结束,
但是线程对象中的run方法会继续执行(以多线程的方式执行)
3.多线程程序运行结果具有随机性
/*
1.定义类继承Thread类
2.Thread类的子类覆盖重写run方法,作用是指定线程要执行的任务
*/
public class SubThread01 extends Thread {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("SubThread01...."+i);
}
}
}
//测试类
public class Demo02Thread {
public static void main(String[] args) {
//3.创建Thread类的子类对象
SubThread01 st01 = new SubThread01();
//4.Thread类的子类对象调用start方法,开启线程
//st01.run();//如果调用run方法,那么就是普通的方法调用,没有多线程的执行效果
st01.start();
for (int i = 1; i <= 100; i++) {
System.out.println("main~~~~~~~~~~~~~"+i);
}
/* Thread t = new Thread();
t.start();*/
}
}
2.2 多线程程序运行过程
2.3 继承Thread类开启线程源码分析
思考:
既然Thread是线程类,为什么非得创建Thread的子类对象,并开启线程呢?
Thread t = new Thread();
t.start();
直接创建Thread对象并开启线程,代码肯定没有问题,但是没有运行结果
原因:
Thread类中的run方法指定的线程任务,不是我们想要的(理解: 什么代码都不行),
所以要继承Thread,重写run方法,指定我们自己的线程任务
继承Thread类开启线程源码分析
Thread类中成员变量:
private Runnable target;//接口类型,必然传递实现类对象
目前使用Thread类的空参构造创建对象:
public Thread() {
//第二个参数: null
init(null, null, "Thread-" + nextThreadNum(), 0);
}
init方法:第二个参数Runnable接口类型
//调用init时,第二个参数传递的是null
//方法调用完毕以后: target == null
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
线程对象调用start方法(Thread类内部定义的),开启线程
start方法,内部调用start0方法
start0方法,是native修饰的本地方法
private native void start0()
该方法内部会调用系统资源,开启线程,调用Thread的run方法
Thread类run方法源码
public void run() {
//因为空参构造创建Thread对象时,target是null target != null false
if (target != null) {
target.run();
}
}
总结:
空参构造创建Thread对象,Thread类内部成员变量Runnable接口初始值null
调用start方法开启线程,会调用run方法,内部判断Runnable是否为null,如果Runnable不是null,
调用Runnable接口实现类对象的run方法
如果创建的是Thread子类对象,覆盖重写了Thread类中的run方法
Thread子类对象调用start方法开启线程,内部调用Thread子类覆盖重写后的run方法
2.4 多线程的内存解释
1.线程对象调用start方法,start方法通知JVM为当前线程对象开辟新的方法执行栈空间,
执行当前线程对象的run方法
2.每个线程的run方法执行栈空间属于线程私有
3.某一个线程运行中出现异常,不会影响其它线程的执行
//Thread类的子类
public class SubThread02 extends Thread {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
//获取线程名称
String threadName = super.getName();
System.out.println(threadName+"....."+i);
if(threadName.equals("Thread-0")&&(i==50)) {
throw new RuntimeException("我要挂掉了,拜拜兄弟们..");
}
}
}
}
//测试类
public class Demo02Thread {
public static void main(String[] args) {
//创建2个线程对象
SubThread02 st01 = new SubThread02();
SubThread02 st02 = new SubThread02();
SubThread02 st03 = new SubThread02();
//调用start方法开启线程
st01.start();
st02.start();
st03.start();
//throw new RuntimeException();
}
}
2.5 获取线程名称
获取线程名称:
1.Thread类的成员方法
public String getName(): 返回该线程的名称。
public void setName(String name): 设置该线程的名称。
public Thread(String name): 通过构造方法指定线程名称
2.如何获取main线程的名称呢?
Thread类的静态方法 更灵活,建议使用
public static Thread currentThread(): 返回对当前正在执行的线程对象。
此方法currentThread在哪个线程中执行,获取到的就是那个线程对象
//Thread的子类
public class SubThread03 extends Thread {
public SubThread03(String name) {
super(name);
}
@Override
public void run() {
//获取线程名称