1. 线程创建
1.1 创建一个类, 继承Thread, 重写run方法
java">class MyThread extends Thread {@Overridepublic void run() {while (true) {System.out.println("hello thread");try {// 这里只能 try catch, 不能 throws.// 此处是方法重写. Java 的方法重写规则不允许子类抛出比父类方法更多的受检查异常,因为这会破坏方法的调用契约。// Thread 的 run() 方法没有声明抛出任何异常,因此子类重写时也不能抛出受检查的异常。// 在重写的时候, 也就不能 throws 异常了.Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class demo1 {public static void main(String[] args) throws InterruptedException {MyThread myThread = new MyThread();myThread.start();// myThread.run();while (true) {System.out.println("hello main");Thread.sleep(1000);}}
}
1.2 创建一个类, 实现Runnable, 重写run方法
java">class MyRunnable implements Runnable{@Overridepublic void run() {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class demo2 {public static void main(String[] args) throws InterruptedException {Runnable runnable = new MyRunnable();Thread t = new Thread(runnable);t.start();while (true) {System.out.println("hello main");Thread.sleep(1000);}}
}
Thread这里是直接把要完成的工作,放到了Thread 的run方法中.
Runnable这里则是分开了,把要完成的工作放到Runnable 中, 再让Runnable和Thread配合.
这种方法就是把线程要执行的任务和线程本身,进一步的解耦合了.
啥是解耦合? 通俗来说就是把之前关系很紧的东西分开了,这样它们之间的相互依赖减少了。
当我们使用并发编程的方式,来完成某个工作时, 就可以用Runnable描述这个工作的具体细节.
使用多线程的方式,就可以使用Runnable搭配线程使用.
使用线程池的方式,就可以使用Runnable搭配线程池使用.
使用协程的方式,也可以使用Runnable搭配协程...
1.3 继承 Thread, 重写run方法, 基于匿名内部类
java">// 通过匿名内部类, 创建线程
public class demo3 {public static void main(String[] args) {Thread t = new Thread() {@Overridepublic void run() {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};t.start();while (true) {System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
1.4 实现 Runnable, 重写run方法, 基于匿名内部类
java">public class Demo4 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}
}
1.5 使用lambda表达式,表示run方法的内容
java">public class Demo5 {public static void main(String[] args) {Thread t = new Thread(() -> {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}
}
lambda表达式, 本质上就是一个"匿名函数". 这样的匿名函数, 主要就可以用来作为回调函数来使用.
回调函数, 不需要咱们程序猿自己主动调用而是在合适的时机, 自动的被调用.
上述的"回调函数"就是在线程创建之后, 才能真正地执行.
.经常会用到回调函数的场景:
1.服务器开发:服务器收到一个请求,触发一个对应的回调函数.
2.图形界面开发:用户的某个操作,触发一个对应的回调.
线程的创建并不只是这五种写法
还有基于Callable和基于线程池.
2. Thread 类及常⻅⽅法
这部分推荐大家去看官方文档, https://docs.oracle.com/javase/8/docs/api/
有一些需要注意的点
给线程起个名字
java">public class Demo6 {public static void main(String[] args) {Thread t = new Thread(() -> {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}, "myThread");t.start();}
}
上述代码运行时, 在jconsole中观察没有main线程了, 却有myThread线程.
Thread中的常见属性
后台线程也叫做守护线程, 后台进程不会影响进程结束
前台线程, 前台进程会影响进程结束, 如果前台进程没执行完, 进程是不会结束的
一个进程中所有的前台线程都执行完, 退出了,此时即使存在后台线程仍然没执行完, 也会随着进程一起退出.
我们手动创建的线程默认是前台进程, 可以通过setDaemo显示的设置成后台进程.
java">// 后台线程 和 前台线程
public class Demo7 {public static void main(String[] args) {Thread t = new Thread(() -> {while (true) {System.out.println("hello thread");}});// 设置成后台线程了.t.setDaemon(true);t.start();try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}
}
线程是否存活, 这个线程指的是Thread对象对应在系统内核的那个线程.
Thread对象的生命周期, 并不是和系统中的线程完全一致.
一般都是Thread对象,先创建好, 再手动调用start, 内核才真正创建出线程.
消亡的时候, 可能是thread 对象先结束了生命周期(没有引用指向这个对象)
也可能是thread 对象还在, 内核中的线程把 run 执行完了,线程就结束了.
启动一个线程 start()
3. 线程终止
那么终止线程应该怎么做呢?
1) 程序猿手动设置标志位
通过这个手动设置的标志位, 来让run尽快结束.
java">// 线程终止
public class Demo8 {// 写作成员变量就不是触发变量捕获的逻辑了. 而是 "内部类访问外部类的成员" , 本身就是 ok 的~~public static boolean isQuit = false;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (!isQuit) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();// 主线程这里执行一些其他逻辑之后, 要让 t 线程结束.Thread.sleep(3000);// 这个代码就是在修改前面设定的标志位.isQuit = true;System.out.println("把 t 线程终止");}
}