• 多线程------实际篇


      目录  

    一.多线程的创建

    1.如何实现多线程呢?

    2.多线程程序实现的方式

    方式1  (参考Thread类)

    run()和start()方法哪个是启动线程的方法?线程能不能多次启动?为什么要重写run方法?

    方式2  (实现Runnable接口)

    方式3  (实现 Callable 接口)

    二.多线程中常用到的方法

    1.Thread类的基本获取和设置方法

    2.线程中的方法(均由线程对象调用)

    三.关于多线程的困扰问题

    1.有的时候我们给线程设置了指定的优先级,但是该线程并不是按照优先级高的线程执行,那是为什么呢?

    2.线程默认的优先级是多少呢?

    3.礼让线程为什么不是理想状态下的一个线程执行一次?

    4.用户线程和守护线程的区别以及守护线程的适用场景?


    多线程小编已经在上一篇文章中详细介绍过了!接下来就看看多线程如何创建以及其中的方法等等!

    一.多线程的创建

    1.如何实现多线程呢?

    由于线程是依赖进程而存在的,所以我们应该先创建一个进程(JVM)出来。而进程是由系统创建的,所以我们应该  去调用系统功能创建一个进程。  但是Java是不能直接调用系统的,所以我们没有办法直接实现多线程程序。但是, Java可以去调用C/C++写好的程序来实现多线程程序。由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,最后  提供一些类供我们使用,   就可以  实现多线程程序  了。

    2.多线程程序实现的方式

    方式1  (参考Thread类)

    • 定义一个类,继承 Thread类
    • 重写 Thread 类中的run方法
    • 创建你定义的这个类对象,调用start()方法启动线程

    那么问题来啦!接招吧~~~~

    run()和start()方法哪个是启动线程的方法?线程能不能多次启动?为什么要重写run方法?

            可不要觉得这都so easy!也会有朋友不懂这些问题的!首先启动线程使用的不是run方法,而是  start方法.  直接用线程.run()是无法成功启动线程的!咱们创建好线程后便调用start方法启动线程,随后由Java 虚拟机调用该线程的 run 方法. 还有针对  同一个线程对象不要重复调用 start().  注意是线程对象!同一个线程不同线程对象的话是可以多次启动的!那么为什么非要重写run方法?如果我们不重写Thread类中的run方法,run()则将执行Thread类自身原有的方法,并且由于Thread类run()的实现为空,因此不会获得任何输出。如果你想  run方法里面实现你想实现的功能,  拿当然就得重写啦!

    1. public class MyThread extends Thread { //继承Thread类
    2. //重写run方法
    3. @Override
    4. public void run() {
    5. System.out.println("具体需要实现的功能!");
    6. }
    7. }
    8. public class MyTest {
    9. public static void main(String[] args) {
    10. MyThread th = new MyThread(); //创建对象
    11. //th.run(); //这样不是开启了线程,你这是创建了一个对象,调用了run方法,没有新的线程开启。
    12. th.start(); //调用start()方法启动线程
    13. // th.start(); 同一个线程对象不要重复调用 start()
    14. MyThread th2 = new MyThread(); //新的线程对象
    15. th2.start();
    16. }
    17. }

    方式2  (实现Runnable接口)

    • 定义一个类,实现  Runnable 接口, 重写其中的run方法
    • 创建定义的这个类的对象
    • new Thread 把这个类的对象作为参数,传过来
    • 调用start()方法启动

    这个方法的  优点在于扩展性强,  实现一个接口的同时还可以再去继承其他类. 可以避免由于Java单继承带来的局限性.

    1. public class MyRunnable implements Runnable { //实现Runnable 接口
    2. //重写run方法
    3. @Override
    4. public void run() {
    5. System.out.println("具体实现的功能!");
    6. }
    7. }
    8. public class MyTest {
    9. public static void main(String[] args) {
    10. MyRunnable myRunnable = new MyRunnable(); //创建对象
    11. Thread th = new Thread(myRunnable); //new Thread 把这个类的对象作为参数,传过来
    12. th.start(); //调用start()方法启动
    13. }
    14. }

    方式3  (实现 Callable 接口)

    • 创建一个类实现Callable 接口
    • 创建一个FutureTask类将Callable接口的子类对象作为参数传进去
    • 创建Thread类,将FutureTask对象作为参数传进去
    • 开启线程

    注意:  实现 Callable 接口相较于实现 Runnable 接口的方式,  方法可以有返回值,并且可以抛出异常.    执行 Callable 方式,  需要 FutureTask 实现类的支持,  用于接收运算结果.  FutureTask 是  Future 接口的实现类.并且Runnable 他的run方法,没有返回值,也不能抛出异常. 而Callable 他的call方法,有返回值,还可以抛出异常. 如果说咱们想拿到线程执行完后返回的结果,就可以用 Callable 任务.也就是用第三种方式来创建线程哈!

    1. public class MyCallable implements Callable { //实现Callable接口
    2. private int num;
    3. public MyCallable(int num) {
    4. this.num = num;
    5. }
    6. //call方法也是线程将来执行的方法
    7. @Override
    8. public Integer call() throws Exception {
    9. int sum = 0;
    10. for (int i = 1; i <= num; i++) {
    11. sum += i;
    12. }
    13. return sum;
    14. }
    15. }
    16. public class MyTest {
    17. public static void main(String[] args) throws ExecutionException, InterruptedException {
    18. MyCallable myCallable = new MyCallable(100);
    19. //可使用 FutureTask 包装 Callable 或 Runnable 对象。因为 FutureTask 实现了 Runnable,所以可将 FutureTask 提交给 Executor 执行。
    20. FutureTask futureTask = new FutureTask<>(myCallable); //创建一个FutureTask类将Callable接口的子类对象作为参数传进去
    21. Thread th = new Thread(futureTask); //创建Thread类,将FutureTask对象作为参数传进去
    22. th.start(); //开启线程
    23. Integer integer = futureTask.get();
    24. System.out.println(integer);
    25. //获取子线程执行完之后的结果
    26. Integer integer1 = futureTask2.get();
    27. System.out.println(integer1);
    28. }
    29. }

    二.多线程中常用到的方法

    1.Thread类的基本获取和设置方法

    • public final String getName()                                              //获取线程名称
    • public final void setName(String name)                            //设置线程名称(其实通过构造方法也可以给线程起名字)
    • public static Thread currentThread()                                 //获取当前执行的线程并返回对当前正在执行的线程对象的引用

    2.线程中的方法(均由线程对象调用)

    • public final int getPriority()                                           //获取线程的优先级
    • public final void setPriority(int newPriority)               //设置线程的优先级
    • public static void sleep(long millis)                             //线程休眠,让当前线程处于休眠状态
    • public final void join()                                                    //加入线程,也就是等待该线程执行完毕了以后,其他线程才能再次执行.  注意: 在线程启动之后,在调用该方法.可以把多个线程并发执行,变为串行
    • public static void yield()                                                //礼让线程也就是暂停当前正在执行的线程对象,并执行其他线程。
    • public final void setDaemon(boolean on)                    //把该线程标记为守护线程.  该方法必须在启动线程前调用
    • public final void stop()                                                  //中断线程,停止线程的运行
    • public void interrupt():                                                  //当线程调用wait(),sleep(long time)方法的时候处于阻塞状态,可以通过这个方法清除阻塞
    1. public class MyTest {
    2. public static void main(String[] args) throws InterruptedException {
    3. System.out.println("主线程中代码AAA");
    4. Thread thObj = Thread.currentThread(); //currentThread();获取当前执行的线程对象
    5. thObj.setName("主线程"); //设置线程名称
    6. System.out.println(thObj.getName()); //获取线程名称
    7. //开启一个子线程
    8. MyThread th1 = new MyThread();
    9. th1.setName("范冰冰");
    10. th1.setPriority(Thread.MAX_PRIORITY); //设置线程的优先级
    11. int priority = th1.getPriority(); //获取线程的优先级
    12. System.out.println("th1的优先级:" + priority);
    13. th1.start();
    14. //再开启一个子线程
    15. MyThread th2 = new MyThread();
    16. th2.setName("刘亦菲");
    17. th2.setPriority(Thread.MIN_PRIORITY);
    18. int priority1 = th2.getPriority();
    19. System.out.println("th2的优先级:" + priority1);
    20. th2.start();
    21. th2.stop(); //强制停止线程
    22. //让当前线程休眠
    23. Thread.sleep(1000 * 3); //休眠之后的代码不再执行
    24. }
    25. }
    26. public class MyThread extends Thread {
    27. public MyThread() {
    28. }
    29. @Override
    30. public void run() {
    31. for (int i = 0; i < 100; i++) {
    32. System.out.println(Thread.currentThread().getName() + "-子线程执行的代码:" + i);
    33. }
    34. }
    35. }

    加入线程join()方法演示 

    1. public class MyTest {
    2. public static void main(String[] args) throws InterruptedException {
    3. MyThread th1 = new MyThread("刘备");
    4. MyThread th2 = new MyThread("关羽");
    5. MyThread th3 = new MyThread("张飞");
    6. th1.start();
    7. th1.join();
    8. th2.start();
    9. th2.join();
    10. th3.start();
    11. th3.join();
    12. //join()在线程开启之后,调用,可以把多个线程并发执行,变为串行。
    13. }
    14. }
    15. public class MyThread extends Thread {
    16. public MyThread() {
    17. }
    18. public MyThread(String name) {
    19. super(name);
    20. }
    21. @Override
    22. public void run() {
    23. for (int i = 0; i < 100; i++) {
    24. System.out.println(this.getName() + "-子线程执行的代码:" + i);
    25. }
    26. }
    27. }

    礼让线程yield()方法演示 

    1. public class MyTest {
    2. public static void main(String[] args) throws InterruptedException {
    3. MyThread th1 = new MyThread("刘备");
    4. MyThread th2 = new MyThread("关羽");
    5. th1.start();
    6. th2.start();
    7. }
    8. }
    9. public class MyThread extends Thread {
    10. public MyThread() {
    11. }
    12. public MyThread(String name) {
    13. super(name);
    14. }
    15. @Override
    16. public void run() {
    17. for (int i = 0; i < 100; i++) {
    18. System.out.println(this.getName() + "-子线程执行的代码:" + i);
    19. Thread.yield(); //线程礼让
    20. }
    21. }
    22. }

    设置守护线程案例演示 

    1. public class MyTest {
    2. public static void main(String[] args) throws InterruptedException {
    3. Thread main = Thread.currentThread();
    4. main.setName("刘备");
    5. for (int i = 0; i < 10; i++) {
    6. System.out.println(main.getName() + "i");
    7. }
    8. MyThread th2 = new MyThread("关羽");
    9. MyThread th3 = new MyThread("张飞");
    10. //设置守护线程 当用户线程死亡后,守护线程也要立即死亡
    11. th2.setDaemon(true);
    12. th2.start();
    13. //设置守护线程
    14. th3.setDaemon(true);
    15. th3.start();
    16. }
    17. }
    18. public class MyThread extends Thread {
    19. public MyThread() {
    20. }
    21. public MyThread(String name) {
    22. super(name);
    23. }
    24. @Override
    25. public void run() {
    26. for (int i = 0; i < 1000; i++) {
    27. System.out.println(this.getName() + "-子线程执行的代码:" + i);
    28. }
    29. }
    30. }

    清楚阻塞状态interrupt()演示 

    1. public class MyTest {
    2. public static void main(String[] args) throws InterruptedException {
    3. MyThread th = new MyThread("线程A");
    4. th.start();
    5. th.interrupt(); //清除线程阻塞的状态
    6. }
    7. }
    8. public class MyThread extends Thread {
    9. public MyThread() {
    10. }
    11. public MyThread(String name) {
    12. super(name);
    13. }
    14. @Override
    15. public void run() {
    16. try {
    17. Thread.sleep(5 * 1000);
    18. } catch (InterruptedException e) {
    19. e.printStackTrace();
    20. }
    21. for (int i = 0; i < 1000; i++) {
    22. System.out.println(this.getName() + "-子线程执行的代码:" + i);
    23. }
    24. }
    25. }

    三.关于多线程的困扰问题

    1.有的时候我们给线程设置了指定的优先级,但是该线程并不是按照优先级高的线程执行,那是为什么呢?

            因为线程的优先级的大小  仅仅表示这个线程被CPU执行的概率增大  了.但是我们都知道多线程  具有随机性,  所以有的时候一两次的运行说明不了问题的!

    2.线程默认的优先级是多少呢?

            如果事先并没有给线程设置优先级,而java采用的又是抢占式调度模型,那么这个线程肯定存在一个默认的优先级.通过getPriority()方法获取线程的优先级.就可以发现线程的  默认优先级是5.  其次线程的  优先级范围是 1---10.  这就不用说啦,肯定是10 的优先级最大咯!

    3.礼让线程为什么不是理想状态下的一个线程执行一次?

            按照我们的想法,这个礼让应该是一个线程执行一次,但是通过测试就发现该效果并不明显.礼让线程是要暂停当前正在执行的线程,这个  暂停的时间是相当短 的,如果在这个线程暂停完毕以后,其他的线程还没有抢占到CPU的执行权,那么这个时候这个线程就会再次和其他线程抢占CPU的执行权.

    4.用户线程和守护线程的区别以及守护线程的适用场景?

            用户线程和守护线程都是线程,区别是Java虚拟机在所有用户线程dead后,程序就会结束.而不管是否还有守护线程还在运行,若守护线程还在运行,也会马上结束. 也就是说  当用户线程dead后,守护线程也要立即dead.  由两者的区别及dead时间点可知,守护线程不适合用于输入输出或计算等操作,因为用户线程执行完毕,程序就dead了,  适用于辅助用户线程的场景,  如JVM的垃圾回收,内存管理都是守护线程,还有就是在做数据库应用的时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监听连接个数、超时时间、状态等.


    (小编也在努力学习更多哟!以后再慢慢分享的啦!)

    希望对友友们有所帮助!!!!

  • 相关阅读:
    分布式多进程加速DQN算法
    电子科大软件系统架构设计——系统规划
    玩转Mysql系列 - 第22篇:mysql索引原理详解
    java4.23学习总结
    测试员突破瓶颈指南,不看又废了一年
    在uniapp微信小程序中保存图片到本地相册
    尚好房 10_Spring Security
    gdb调试
    苏东坡在元丰五年
    Spring(二)
  • 原文地址:https://blog.csdn.net/naoguoteng/article/details/125854750