• java编程基础总结——29.多线程编程


    一、基本概念

    系统编程 - 多线程编程

    1. Unix操作系统:
            第一款多任务多用户操作系统
            现代操作系统之父
           注意: 在多核CPU诞生之前(单核CPU下),所有的多任务本质都是一种伪多任务。(并没                    有真正的实现多任务,只是来回切换运行,cpu速度很快,人肉眼感受不到)
                      多核CPU下,就可以实现真正意义上的多任务!!


    2. 并发:多个任务(进程)抢占少量资源
        并行:每个资源有一个独立cpu,之间形成并行

        多核下才能有并行,单核只有并发

    3. 让每个任务得到合理运行时间,防止一个任务持续抢占资源 和 先后顺序问题:

    时间片:每一个任务每次在CPU中占有的时间
    优先级别调度:任务有先后顺序问题,核心软件需要先运行(是为其他服务的)

    注意:操作底层的多任务使用的就是时间片轮换机制,配合优先级别调度完成。(多核也一样,核再多也不太可能让每一个任务都单独分配到cpu)

    首先看优先级别(也会根据重要性分配时间片) 。当每次结束后,优先级别-1,和其他任务一起再进行抢

    4. 多任务:
        多进程编程
        多线程编程
        协程编程

    5. 多线程编程:
    java自身提供了四种创建多线程的方案:

    jdk5之前
        1、继承Thread
        2、实现Runable接口

    jdk5之后
        3、实现Callable接口
        4、线程池实现多线程

    推荐使用第2,3 种,不太推荐第1种。因为java是单继承机制,一旦某个类继承了一个类就不能继承其他类

    而接口可以多继承

    二、java自身四种创建多线程的方案:
        

    1、继承Thread

    某个类继承了线程类,这个类就会自动的成为一个线程类

    单线程情况下,没有办法直接让多个任务同时执行,下面的代码唱歌输出结束后才会输出跳舞

    1. package com.openlab.multithreading;
    2. public class TestIdea {
    3. public static void main(String[] args) {
    4. // 单线程情况下,没有办法直接让多个任务同时执行
    5. singe();
    6. dance();
    7. }
    8. public static void singe() {
    9. for (int i = 0; i < 100; i++) {
    10. System.out.println("我开始唱歌了——"+ i);
    11. }
    12. }
    13. public static void dance() {
    14. for (int i = 0; i < 100; i++) {
    15. System.out.println("我开始跳舞了——"+ i);
    16. }
    17. }
    18. }

    继承Thread:

    若对象调用方法的话还是先子线程,再主线程,还是不对,和之前一样

    需要调用start()方法

    1. package Testthread;
    2. public class TestThread {
    3. public static void main(String[] args) {
    4. MyThread01 thread = new MyThread01();
    5. thread.run();
    6. for (int i = 0; i < 10000; i++) {
    7. System.out.println("主线程正在运行");
    8. }
    9. }
    10. }
    11. class MyThread01 extends Thread{
    12. @Override
    13. public void run() {
    14. for (int i = 0; i < 10000; i++) {
    15. System.out.println("子线程正在运行");
    16. }
    17. }
    18. }

     调用start()方法:

    注意:main函数也是线程,是主线程

    1. package Testthread;
    2. public class TestThread {
    3. public static void main(String[] args) {
    4. MyThread01 thread = new MyThread01();
    5. //调用start方法
    6. thread.start();
    7. for (int i = 0; i < 10000; i++) {
    8. System.out.println("主线程正在运行");
    9. }
    10. }
    11. }
    12. class MyThread01 extends Thread{
    13. @Override
    14. public void run() {
    15. for (int i = 0; i < 10000; i++) {
    16. System.out.println("子线程正在运行");
    17. }
    18. }
    19. }

    结果会子线程、主线程交替执行,结果不可控 

    2、实现Runable接口

    Runable接口只有一个方法run(),没有start()方法

    但是Thread有个构造方法可以传入一个Runable对象,我们可以由此构建

    1. package Testthread;
    2. public class TestRunnable {
    3. public static void main(String[] args) {
    4. MyThread02 thread01 = new MyThread02();
    5. MyThread02 thread02 = new MyThread02();
    6. // 使用Runnable接口实现的多线程类,该类的对象没有start方法
    7. // 所以启动线程对象,必须借助Thread类的构造函数
    8. new Thread(thread01).start();
    9. new Thread(thread02).start();
    10. }
    11. }
    12. class MyThread02 implements Runnable {
    13. @Override
    14. public void run() {
    15. for (int i = 0; i < 10000; i++) {
    16. //每个线程都是有名字的
    17. //可以利用Thread.currentThread()当前线程获取当前线程的名字
    18. System.out.println(Thread.currentThread().getName() + "子线程正在运行");
    19. }
    20. }
    21. }

    3、实现Callable接口

    Callable接口也只有一个方法call(),有返回值。

    也没有start()方法,而且Thread也没有一个构造方法里面有Callable

    启动对应的线程对象需要用Future

    FutureTask是Future的子类

    FutureTask继承了RunnableFuture

    RunnableFuture继承了Runnable和Future

    即RunnableFuture是Runnable的子类,那么FutureTask也是Runnable的子类,我们就可以使用FutureTask

    利用多态,接口式编程

     

    1. package Testthread;
    2. import java.util.concurrent.Callable;
    3. import java.util.concurrent.Future;
    4. import java.util.concurrent.FutureTask;
    5. public class TestCallable {
    6. public static void main(String[] args) {
    7. MyThead03 thread03 = new MyThead03();
    8. FutureTask futureTask = new FutureTask(thread03);
    9. new Thread(futureTask).start();
    10. for (int i = 0; i < 10000; i++) {
    11. System.out.println(Thread.currentThread().getName() +"主线程开始运行");
    12. }
    13. }
    14. }
    15. class MyThead03 implements Callable {
    16. int sum = 0;
    17. @Override
    18. public Integer call() throws Exception {
    19. for (int i = 0; i < 10000; i++) {
    20. sum += i;
    21. System.out.println("使用Callable接口创建的子线程"+ i);
    22. }
    23. return sum;
    24. }
    25. }

    使用callable接口实现的多线程对象,最后能够得到线程方法返回的结果 

    即这个例子中的当前子线程结束,会返回一个结果

    用get()方法拿到

    1. package com.openlab.multithreading;
    2. import java.util.concurrent.Callable;
    3. import java.util.concurrent.ExecutionException;
    4. import java.util.concurrent.FutureTask;
    5. import java.util.concurrent.RunnableFuture;
    6. public class TestCallable {
    7. public static void main(String[] args) {
    8. MyThread2 mt = new MyThread2();
    9. FutureTask futureTask = new FutureTask(mt);
    10. new Thread(futureTask).start();
    11. // 使用callable接口实现的多线程对象,最后能够得到线程方法返回的结果
    12. try {
    13. Integer res = (Integer) futureTask.get();
    14. System.out.println("线程方法返回的结果:" +res);
    15. } catch (InterruptedException e) {
    16. e.printStackTrace();
    17. } catch (ExecutionException e) {
    18. e.printStackTrace();
    19. }
    20. for (int i = 0; i < 10000; i++) {
    21. System.out.println(Thread.currentThread().getName() +"主线程开始运行");
    22. }
    23. }
    24. }
    25. class MyThread2 implements Callable {
    26. @Override
    27. public Integer call() throws Exception {
    28. int sum = 0;
    29. for (int i = 0; i < 10000; i++) {
    30. sum += i;
    31. System.out.println("使用Callable接口创建的子线程"+ i);
    32. }
    33. return sum;
    34. }
    35. }


        

    三、线程对象的一些常见方法:

    Thread的run()和start()区别:

    run()是一个线程方法,该方法里面的代码是线程直接执行时需要执行的代码,但是不能直接调用,否则就是普通方法了。

    需要start()去启动run()方法

    getName        //获取当前线程名称
    getId              //获取线程编号(每个线程一旦启动,编号是固定的)

                            PID,进程编号,操作系统为每一个进程分配的编号,也是唯一的
    getPriority      //获取线程的优先级。最小的优先级是1,最大是10

    getState         //获取线程状态,有六种状态

                          尚未启动(NEW),运行(RUNNABLE),阻塞(BLOCKED),等待(WAITING),具有                            指定等待时间的某一等待线程的线程状态(TIMED_WAITING),终止(TERMINATED)
    interrupt         //线程中断,调用不会中断当前线程,表示要抛出一个中断信号
    stop               //早在jdk2.0就已过时,因为是暴力终止线程,应该根据自己的逻辑代码去终止
    isAlive           //判断线程是否活着
    join                //阻塞当前线程,不再成并发效果,其他线程不会跟此线程进行资源抢夺,让当前线                         程执行结束。

    setDaemon    //若为true表示将当前线程设为守护线程。

                            守护线程依赖于一个线程存在,这个线程启动就启动,关闭就关闭

    1. package Testthread;
    2. public class TestThread {
    3. public static void main(String[] args) {
    4. MyThread myThread = new MyThread();
    5. myThread.start();
    6. for (int i = 0; i < 10000; i++) {
    7. System.out.println("主线程正在运行");
    8. }
    9. }
    10. }
    11. class MyThread extends Thread{
    12. @Override
    13. public void run() {
    14. for (int i = 0; i < 10000; i++) {
    15. System.out.println("子线程正在运行");
    16. }
    17. }
    18. }

    以下代码都是写在main线程中的 

    // 获取线程名称
    System.out.println(myThread.getName());
    myThread.setName("豪豪");
    System.out.println(myThread.getName()); 

    //获取线程编号

    //没有setID

    System.out.println(myThread.getId());

    //获取线程的优先级

    System.out.println(myThread.getPriority());
    myThread.setPriority(6);
    System.out.println(myThread.getPriority());
    System.out.println(Thread.currentThread().getPriority());

      

     System.out.println(myThread.isAlive());

    myThread.join(); 

    四、Thread类的静态方法:

    1. public static void sleep(long millis):让线程进入休眠状态

    使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),毫秒结束后将会继续执行

    1. // java的线程类
    2. class MyThread extends Thread {
    3. @Override
    4. public void run() {
    5. // 要执行的代码写在这儿
    6. for (int i = 0; i < 100; i++) {
    7. if (i == 5) {
    8. try {
    9. // 让线程进入休眠状态
    10. Thread.sleep(20);//传的是毫秒数
    11. } catch (InterruptedException e) {
    12. e.printStackTrace();
    13. }
    14. }
    15. System.out.println(Thread.currentThread().getName() + "一个独立的子线程运行了" + i);
    16. }
    17. }
    18. }

        

    2. 除了setName(),构造函数,也可以传名字

    我们用的是Thread的子类,所以我们使用的话需要写构造函数

    主函数不能构造,所以主函数只能通过setName()

    //主函数修改名字

    Thread.currentThread().setName("主函数");

    1. package com.openlab.multithreading;
    2. public class TestThread {
    3. public static void main(String[] args) throws InterruptedException {
    4. MyThread myThread = new MyThread("张三");
    5. MyThread myThread02 = new MyThread("李四");
    6. }
    7. // java的线程类
    8. class MyThread extends Thread {
    9. public MyThread() {
    10. }
    11. public MyThread(String name) {
    12. super(name);
    13. }
    14. @Override
    15. public void run() {
    16. // 要执行的代码写在这儿
    17. for (int i = 0; i < 100; i++) {
    18. if (i == 5) {
    19. try {
    20. // 让线程进入休眠状态
    21. Thread.sleep(20);//传的是毫秒数
    22. } catch (InterruptedException e) {
    23. e.printStackTrace();
    24. }
    25. }
    26. System.out.println(Thread.currentThread().getName() + "一个独立的子线程运行了" + i);
    27. }
    28. }
    29. }

    3. yield()             

    暗示当前线程放弃一次抢占。只是暗示,当前线程是自由的,它可以忽视这个暗示

                          

  • 相关阅读:
    第九课 排序
    下拉框comobox--可勾选多个表项check
    软件项目管理课后习题——第3章软件项目的启动过程
    牛客-TOP101-BM43
    【Visual Leak Detector】QT 中 VLD 输出解析(二)
    Spring Bean的加载方式
    BEANZ NFT 概览与数据分析
    奖金最高15000美元!微软宣布Bing AI漏洞赏金计划
    QT实现TCP服务器客户端搭建的代码,现象
    java毕业设计校园墙系统mybatis+源码+调试部署+系统+数据库+lw
  • 原文地址:https://blog.csdn.net/m0_58679504/article/details/126331513