1.多线程和进程
进程:在内存中执行的应用程序
线程:是进程中最小的执行单元
线程作用:负责当前进程中程序的运行.一个进程中至少有一个线程,一个进程还可以有多个线程,这样的应用程序就称之为多线程程序
简单理解:一个功能就需要一条线程取去执行
1.使用场景: 软件中的耗时操作 -> 拷贝大文件, 加载大量的资源
所有的聊天软件
所有的后台服务器
一个线程可以干一件事,我们就可以同时做多件事了,提高了CPU的利用率
2.并发和并行
并行:在同一个时刻,有多个执行在多个CPU上(同时)执行(好比是多个人做不同的事儿)
比如:多个厨师在炒多个菜
并发:在同一个时刻,有多个指令在单个CPU上(交替)执行
比如:一个厨师在炒多个菜
细节:
1.之前CPU是单核,但是在执行多个程序的时候好像是在同时执行,原因是CPU在多个线程之间做高速切换
2.现在咱们的CPU都是多核多线程的了,比如2核4线程,那么CPU可以同时运行4个线程,此时不同切换,但是如果多了,CPU就要切换了,所以现在CPU在执行程序的时候并发和并行都存在
3.CPU调度
1.分时调度:值的是让所有的线程轮流获取CPU使用权,并且平均分配每个线程占用CPU的时间片
2.抢占式调度:多个线程轮流抢占CPU使用权,哪个线程先抢到了,哪个线程先执行,一般都是优先级高的先抢到CPU使用权的几率大,我们java程序就是抢占式调度
4.主线程介绍
主线程:CPU和内存之间开辟的转门为main方法服务的线程
第二章.创建线程的方式(重点)
1.第一种方式_extends Thread
1.定义一个类,继承Thread
2.重写run方法,在run方法中设置线程任务(所谓的线程任务指的是此线程要干的具体的事儿,具体执行的代码)
3.创建自定义线程类的对象
4.调用Thread中的start方法,开启线程,jvm自动调用run方法
public class Test01 {
public static void main(String[] args) {
//创建线程对象
MyThread t1 = new MyThread();
//调用start方法,开启线程,jvm自动调用run方法
t1.start();
for (int i = 0; i < 10; i++) {
System.out.println("main线程..........执行了"+i);
}
}
}
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("MyThread...执行了"+i);
}
}
}
2.多线程在内存中的运行原理
注意:同一个线程对象不能连续调用多次start,如果想要再次调用start,那么咱们就new一个新的线程对象
3.Thread类中的方法
void start() -> 开启线程,jvm自动调用run方法
void run() -> 设置线程任务,这个run方法是Thread重写的接口Runnable中的run方法
String getName() -> 获取线程名字
void setName(String name) -> 给线程设置名字
static Thread currentThread() -> 获取正在执行的线程对象(此方法在哪个线程中使用,获取的就是哪个线程对象)
static void sleep(long millis)->线程睡眠,超时后自动醒来继续执行,传递的是毫秒值
public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { //线程睡眠 try { Thread.sleep(1000L); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(Thread.currentThread().getName()+"...执行了"+i); } } } public class Test01 { public static void main(String[] args) throws InterruptedException { //创建线程对象 MyThread t1 = new MyThread(); //给线程设置名字 t1.setName("wind"); //调用start方法,开启线程,jvm自动调用run方法 t1.start(); for (int i = 0; i < 10; i++) { Thread.sleep(1000L); System.out.println(Thread.currentThread().getName()+"线程..........执行了"+i); } } }
问题:为啥在重写的run方法中有异常只能try,不能throws
原因:继承的Thread中的run方法没有抛异常,所以在子类中重写完run方法之后就不能抛,只能try
4.Thread中其他的方法
- void setPriority(int newPriority) -> 设置线程优先级,优先级越高的线程,抢到CPU使用权的几率越大,但是不是每次都先抢到
- int getPriority() -> 获取线程优先级
- void setDaemon(boolean on) -> 设置为守护线程,当非守护线程执行完毕,守护线程就要结束,但是守护线程也不是立马结束,当非守护线程结束之后,系统会告诉守护线程人家结束了,你也结束吧,在告知的过程中,守护线程会执行,只不过执行到半路就结束了
- static void yield() -> 礼让线程,让当前线程让出CPU使用权
- void join() -> 插入线程或者叫做插队线程
上面这几个方法其实没什么用,对线程的影响比较微弱
5.第二种方式_实现Runnable接口
1.创建类,实现Runnable接口
2.重写run方法,设置线程任务
3.利用Thread类的构造方法:Thread(Runnable target),创建Thread对象(线程对象),将自定义的类当参数传递到Thread构造中 -> 这一步是让我们自己定义的类成为一个真正的线程类对象
4.调用Thread中的start方法,开启线程,jvm自动调用run方法
public class Test01 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
/*
Thread(Runnable target)
*/
Thread t1 = new Thread(myRunnable);
//调用Thread中的start方法,开启线程
t1.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"...执行了"+i);
}
}
}
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"...执行了"+i);
}
}
}
两种的区别:
1.继承Thread:继承只支持单继承,有继承的局限性
2.实现Runnable:没有继承的局限性:如MyThread extends Fu implements Runnable
6.第三种方式_匿名内部类创建多线程
严格意义上来说,匿名内部类方式不属于创建多线程方式其中之一,因为匿名内部类形式建立在实现Runnable接口的基础上完成的
匿名内部类回顾:
1.new 接口/抽象类(){
重写方法
}.重写的方法();
2.接口名/类名 对象名 = new 接口/抽象类(){
重写方法
}
对象名.重写的方法();
那么放在这里:
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"...执行了"+i);
}
}
},"wind").start();