It is during our darkest moments that we must focus to see the light
—— 24.5.24
一、第一种方式_继承extends Thread方法
1.定义一个类,继承Thread
2.重写run方法,在run方法中设置线程任务(所谓的线程任务指的是此线程要干的具体的事儿,具体执行的代码)
3.创建自定义线程类的对象
4.调用Thread中的start方法,开启线程,jvm自动调用run方法package S64thread;public class Demo195Mythread extends Thread{@Overridepublic void run(){for (int i = 0; i < 10; i++) {System.out.println("Mythread执行了"+i);}} }
package S64thread;public class Demo196Test {public static void main(String[] args) {// 创建线程对象Demo195Mythread t1 = new Demo195Mythread();// 调用start方法,开启线程,jvm自动调用run方法,只有调用start才会开启线程,二者一同执行// t1.run();t1.start();for (int i = 0; i < 10; i++) {System.out.println("main线程执行了"+i);}} }
二、多线程在内存中的运行原理
注意:同一个线程对象不能连续调用多次start,如果想要再次调用start,那么咱们就new一个新的线程对象
三、Thread类中的方法
package S65threadMethod;public class Demo197Mythread extends Thread{@Overridepublic void run(){for (int i = 0; i < 10; i++) {System.out.println(getName()+"执行了"+i);}} }
package S65threadMethod;public class Demo198Test {public static void main(String[] args) {// 创建线程对象Demo197Mythread t1 = new Demo197Mythread();// 给线程设置名字t1.setName("赵四");// 调用start方法,开启线程,jvm自动调用run方法,只有调用start才会开启线程,二者一同执行// t1.run();t1.start();for (int i = 0; i < 10; i++) {System.out.println("main线程执行了"+i);}} }
void start() -> 开启线程,jvm自动调用run方法
void run() -> 设置线程任务,这个run方法是Thread重写的接口Runnable中的run方法
String getName() -> 获取线程名字
void setName(string name) -> 给线程设置名字
static Thread currentThread() -> 获取正在执行的线程对象(此方法在哪个线程中使用,获取的就是哪个线程对象static void sleep(long millis) -> 线程睡眠,超时后会自动醒来继续执行,传递的是毫秒值,睡眠时不会影响其他线程,其他线程会直接运行完,不会等待
package S65threadMethod;public class Demo197Mythread extends Thread{@Overridepublic void run(){for (int i = 0; i < 10; i++) {// 线程睡眠try{// 线程sleep后一顿一顿的执行Thread.sleep(1000L);}catch (InterruptedException e){throw new RuntimeException(e);}// 链式调用 currentThread 获取正在执行的线程对象System.out.println(Thread.currentThread().getName()+"线程执行了"+i);}} }
package S65threadMethod;public class Demo198Test {public static void main(String[] args) {// 创建线程对象Demo197Mythread t1 = new Demo197Mythread();// 给线程设置名字 线程一顿一顿的执行t1.setName("赵四");// 调用start方法,开启线程,jvm自动调用run方法,只有调用start才会开启线程,二者一同执行// t1.run();t1.start();for (int i = 0; i < 10; i++) {// 链式调用System.out.println(Thread.currentThread().getName()+"线程执行了"+i);}} }
问题:为啥在重写的run方法中有异常只能try,不能throws
原因:继承的 Thread中的run方法 没有抛异常,所以在子类中重写完run方法之后就不能抛,只能try…catch
四、thread中的其他方法
1.线程优先级
① void setPriority(int newPriority) —— 设置线程优先级,优先级越高的线程,抢到CPU使用权的几率越大,但是不是每次都先抢到
② int getPriority() —— 获取线程优先级
package S66thread;import S65threadMethod.Demo197Mythread;public class Demo200Test {public static void main(String[] args) {// 创建两个线程对象Demo199Mythread1 t1 = new Demo199Mythread1();t1.setName("金莲");Demo199Mythread1 t2 = new Demo199Mythread1();t2.setName("阿庆");// 获取两个线程的优先级 5是一个默认优先级/*获取两个线程的优先级MIN_PRIORITY=1 最小优先级 1NORM_PRIORITY=5 默认优先级 5MAX_PRIORITY=10 最大优先级 10*/System.out.println(t1.getPriority());System.out.println(t2.getPriority());// 设置优先级t1.setPriority(1);t2.setPriority(10);System.out.println(t1.getPriority());System.out.println(t2.getPriority());t1.start();t2.start();} }
2.守护线程
③ void setDaemon(boolean on) —— 设置为守护线程,当非守护线程执行完毕,守护线程就要结束,但是守护线程也不是立马结束,当非守护线程结束之后,系统会告诉守护线程人家结束了,你也结束吧,在告知的过程中,守护线程会执行,只不过执行到半路就结束了
package S67ProtectThread;public class Demo201MyThread1 extends Thread{@Overridepublic void run(){for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"执行了......"+i);}} }
package S67ProtectThread;public class Demo203MyThread2 extends Thread{@Overridepublic void run(){for (int i = 0; i < 99; i++) {System.out.println(Thread.currentThread().getName()+"执行了..."+i);}} }
package S67ProtectThread;public class Demo202Test {public static void main(String[] args) {// 创建两个线程对象Demo201MyThread1 t1 = new Demo201MyThread1();t1.setName("金莲");Demo203MyThread2 t2 = new Demo203MyThread2();t2.setName("阿庆");// 将t2设置为守护线程t2.setDaemon(true);t1.start();t2.start();} }
使用场景:
3.礼让线程
让两个线程尽可能的平衡一点->尽量让两个线程交替执行
④ static void yield() —— 礼让线程,让当前线程让出CPU使用权
场景说明:如果两个线程一起执行,可能会执行一会儿线程A,再执行一会线程B,或可能线程A执行完毕了,线程B在执行注意:只是尽可能的平衡,不是绝对的平衡,有可能在礼让线程之后又抢到了CPU使用权
package S68PoliteThread;public class Demo204MyThread1 extends Thread{@Overridepublic void run(){for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"...执行了..."+i);// 礼让线程Thread.yield();}} }
package S68PoliteThread;public class Demo205Test {public static void main(String[] args) {// 创建两个线程对象Demo204MyThread1 t1 = new Demo204MyThread1();t1.setName("金莲");Demo204MyThread1 t2 = new Demo204MyThread1();t2.setName("阿庆");// 礼让线程Thread.yield();t1.start();t2.start();} }
4.插入线程
⑤ void join() —— 插入线程或者叫做插队线程
package S69InsertThread;public class Demo206MyThread1 extends Thread{@Overridepublic void run(){for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"...执行了..."+i);// 插入线程}} }
package S69InsertThread;public class Demo207Test {public static void main(String[] args) throws InterruptedException {// 创建两个线程对象Demo206MyThread1 t1 = new Demo206MyThread1();t1.setName("金莲");t1.start();// 插入线程 表示把t1插入到当前线程之前t1.join();for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"...执行了..."+i);// 插入线程}t1.start();} }
五、第二种方式_实现Thread中的Runnable接口
Thread中的run方法重写的Runnable中的接口,可以直接实现Runnable接口
1.创建类,实现Runnable接口
2.重写run方法,设置线程任务
3.利用Thread类的构造方法:Thread(Runnable target),创建Thread对象(线程对象),将自定义的类当参数传递到Thread构造中 —> 这一步是让我们自己定义的类成为一个真正的线程类对象4.调用Thread中的start方法,开启线程,jvm虚拟机自动调用run方法
package S70ThreadRunnable;public class MyRunnable implements Runnable{@Overridepublic void run(){for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"...执行了"+i);}} }
package S70ThreadRunnable;public class Demo208Test {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);}} }
六、两种实现多线程的方式区别
1.继承Thread:继承只支持单继承,有继承的局限性
2.实现Runnable:没有继承的局限性,MyThread extends Fu implements Runnable
七、匿名内部类方式创建多线程
严格意义上来说,匿名内部类方式不属于创建多线程方式其中之一,因为匿名内部类形式建立在实现Runnable接口的基础上完成的
匿名内部类回顾:
1.new 接口/抽象类(){重写方法
}.重写的方法();
2.接口名/类名对象名 = new 接口/抽象类(){重写方法
}
对象名.重写的方法();匿名内部类给线程取名
Thread(Runnable target,string name)name指的是给匿名内部类中的线程设置名字
package S70ThreadRunnable;public class Demo209Test02 {public static void main(String[] args) {/*Thread(Runnable r)*/new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"...执行了"+i);}}}).start();new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"...执行了"+i);}}}).start();} }