多线程继承关系: 两种实现方式:①继承Runnable实现子类Thread,重写run()方法,②实现Runnable接口,重写run方法
①继承Thread类创建线程
package com.edu.threaduse; /** * @author mtl121 * @version 1.0 */ public class Thread01 { public static void main(String[] args) throws InterruptedException { //创建一个Cat对象,可以当作线程使用 Cat cat = new Cat(); /* (1) public synchronized void start() { ... start0() ... } (2) //start0() 是本地方法,是 JVM 调用, 底层是 c/c++实现 //真正实现多线程的效果, 是 start0(), 而不是 run private native void start0(); */ cat.start();//启动线程-> 最终会执行 cat 的 run 方法 //当main线程启动一个子线程,主线程main不会阻塞, 会继续执行, 表现为主线程和子线程交替执行 //当主线程结束不会造成进程的结束,如果子线程未结束,线程也不会结束,只有当所有子线程均运行结束,进程才会结束 System.out.println("主线程名:" + Thread.currentThread().getName()); for(int i = 0; i < 50; i++){ System.out.println("主线程 i=" + i); //让主线程休眠1s Thread.sleep(1000); } } } // 当一个类继承了Thread类,该类就可以当作线程使用 // 我们回重写run方法,在run中写上自己的业务代码 // Thread的run方法是重写的Runnable接口的run方法 class Cat extends Thread { int times = 0; @Override public void run() { while (true) { System.out.println("喵喵,我是小猫咪" + (++times) + "进程名:" + Thread.currentThread().getName()); //线程休眠1秒, ctrl + alt + t生成trycatch代码块 try { //让线程休眠1s Thread.sleep(1000);//单位毫秒 } catch (InterruptedException e) { e.printStackTrace(); } if(times == 80){ break;// 当times到8 ,退出while,线程结束 } } } }
主线程可以开多个子线程,子线程还可以开子线程
关于调用Start()启动线程,而不是直接调用run方法的原因:
cat.start();//启动线程,-> 最终会执行 cat 的 run 方法 cat.run();//直接调用run的话,run 方法就是一个普通的方法, 没有真正的启动一个线程,就会把 run 方法执行完毕,才向下执行,并不会交替执行
②实现Runnable接口创建线程
使用这种方法的原因:Java是单继承的,某些情况下一个类可能已经继承了某个父类,这时就不能通过继承Thread类创建线程了,因此提供这种方式来创建线程
有了Thread,为什么还要有Runnable?
1、为多继承提供可能。
对于类继承,java只允许单继承模式,如果你需要设计一个类既继承业务类拥有某个业务类功能,又继承线程对象Thread类具有多线程的功能,那么Thread类就无法满足你的要求。这时候就要选择Runnable。更多的接口优于抽象类的内容可参考《Effect Java》中的第16条原则,这里不赘述。
2、实现数据的共享。
首先我们知道,Thread线程对象调用run()方法是采用回调的机制。其次,每个Thread线程对象只能调用一次start方法启用一个线程。因此,把数据放在Thread对象中,并不能被多个线程所共享。java为我们提供了Runnable接口,就很好的解决了这个问题。我们把要操作的数据封装入Runnable的实现类中,然后将Runnable实例传入多个Thread对象中,就可以实现一个对象数据在多个线程的共享了package com.edu.threaduse; /** * @author mtl121 * @version 1.0 * 通过实现Runnable接口来开发线程 */ public class Thread02 { public static void main(String[] args) { // Dog dog = new Dog(); // //dog.start(); 不能调用start方法 // //创建了 Thread 对象,把 dog 对象(实现 Runnable),放入 Thread,底层使用了代理设计模式 // Thread thread = new Thread(dog); // thread.start(); Tiger tiger = new Tiger();//Tiger实现了Runnable接口 ThreadProxy threadProxy = new ThreadProxy(tiger);//传入tiger threadProxy.start(); } } class Animal{} class Tiger extends Animal implements Runnable{ @Override public void run() { System.out.println("老虎嗷嗷叫"); } } //线程代理类, 实现了简单的Thread类 class ThreadProxy implements Runnable { private Runnable target = null; @Override public void run() { if(target != null){ target.run(); } } public ThreadProxy(Runnable target) { this.target = target; } public void start(){ start0(); } private void start0() { run(); } } class Dog implements Runnable { int count = 0; @Override public void run() { while(true){ System.out.println("小狗汪汪叫" + (++count) + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if(count == 8){ break; } } } }
案例一:实现多线程售票
package com.edu.threaduse; /** * @author mtl121 * @version 1.0 */ public class sellTicket { public static void main(String[] args) { Ticket ticket1 = new Ticket(); ticket1.start(); Ticket ticket2 = new Ticket(); ticket2.start(); Ticket ticket3 = new Ticket(); ticket3.start(); Ticket ticket4 = new Ticket(); ticket4.start(); Ticket ticket5 = new Ticket(); ticket5.start(); //继承Thread和实现Runnable接口的区别 //从Java设计来看,本质一样,Thread类也是实现Runnable接口, //但是从用法上来看,实现Runnable接口更适合多个线程共享一个资源的情况 // Ticket1 ticket1 = new Ticket1(); // new Thread(ticket1).start();//将同一个对象放入不同的线程保证了共享count票数 // new Thread(ticket1).start(); // new Thread(ticket1).start(); } } class Ticket extends Thread{ static int count = 100; @Override public void run() { while(true){ if(count <= 0){ System.out.println("售票结束"); break; } try { Thread.sleep(50); System.out.println(String.format("还剩下%d张票", --count) + Thread.currentThread().getName()); } catch (Exception e) { e.printStackTrace(); } } } } class Ticket1 implements Runnable{ int count = 100; @Override synchronized public void run() { while(true){ count --; System.out.println(String.format("还剩下%d张票", count) + Thread.currentThread().getName()); if(count <= 0){ System.out.println("售票结束"); break; } } } }
线程终止(通知方式)
package com.edu.threaduse; /** * @author mtl121 * @version 1.0 */ public class ThreadExit { public static void main(String[] args) throws InterruptedException { T t1 = new T(); t1.start(); //如果希望main线程控制子线程t1的终止,必须可以修改loop Thread.sleep(1000); //使用通知的方式让t1退出run方法; t1.setLoop(false); } } class T extends Thread{ private int count = 0; //控制变量 private boolean loop = true; @Override public void run() { while(loop){ try { Thread.sleep(50); } catch (InterruptedException e) {//当调用该线程的interrupt方法时,就会catch一个中断异常 e.printStackTrace(); } System.out.println("T 运行中..." + (++count)); } } public void setLoop(boolean loop){ this.loop = loop; } }
注意事项和细节:
①start()底层会创建新的线程,调用run方法,run方法就是一个简单的方法调用,不会启动新线程
②线程优先级范围:
③interrept: 中断进程,但是并没有真正的终止进程,所以一般用于中断正在休眠的进程(打断线程的休眠[sleep])
④sleep :线程的静态方法,用于使当前的线程休眠
package test2; import test1.*; /** * @author mtl121 * @version 1.0 */ public class test2 { public static void main(String[] args) throws InterruptedException { T2 t2 = new T2(); t2.start(); for(int i = 0; i <= 20; i++){ Thread.sleep(1000); System.out.println("主线程 吃了" + i + "包子"); if(i == 5){ // t2.join();//要先启动线程才能join Thread.yield(); } } } } class T2 extends Thread { @Override public void run() { for(int i = 0; i <= 20; i++){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程 吃了" + i + "包子"); } } }
前文说到主线程结束不会导致整个进程的结束,而是要等待所有的线程都执行完毕才能结束进程。但是如果需要完成这样的需求:主线程结束,子线程跟着结束
只需要将子线程设置为守护线程即可:
package com.edu.threaduse; /** * @author mtl121 * @version 1.0 */ public class Thread_home { public static void main(String[] args) throws InterruptedException { for(int i = 0; i < 10; i++){ System.out.println("hi" + (i + 1)); Thread.sleep(1000); if(i == 4){ T2 t2 = new T2(); Thread thread = new Thread(t2); thread.setDaemon(true); thread.start(); } } System.out.println("主线程结束"); } } class T2 implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("hello" + (i + 1)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }