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{ @Override public 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{ @Override public 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{ @Override public 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 最小优先级 1 NORM_PRIORITY=5 默认优先级 5 MAX_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{ @Override public void run(){ for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"执行了......"+i); } } }
package S67ProtectThread; public class Demo203MyThread2 extends Thread{ @Override public 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{ @Override public 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{ @Override public 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{ @Override public 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() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"...执行了"+i); } } }).start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"...执行了"+i); } } }).start(); } }