• 多线程的创建及状态描述


    一、什么是线程

    💡线程是程序执行流的最小单元💕💕. 它包含在进程之中,是进程中的实际运行单位,在Unix System V及SunOS中也被称为轻量进程(lightweight processes). 一个进程中可以并发多个线程,每个线程并行执行不同的任务🕛🕛🕛. 虽然多进程也能实现并发编程, 但是线程比进程更轻量(创建线程比创建进程更快,销毁线程比销毁进程更快,调用线程比调用进程更快 ).🫠🫠🫠


    进程和线程的区别:

    • 进程里面包含线程, 每个进程至少包含一个线程,即主线程.
    • 同一个进程的线程之间可以共享同一个内存空间,两个进程之间不能共享同一块内存空间.
    • 进程是系统分配资源的最小单位,线程是系统调度的最小单位.

    二、多线程使用场景

    1.专用服务器

    🙈🙈🙈 比如用于大型网游服务器的搭建,游戏画面实际需要 CPU 大量的在背后运算,使用多线程,可以更好的利用CPU多核计算资源,从而提高效率!!

    2.异步处理

    👁️‍🗨️👁️‍🗨️👁️‍🗨️比如发博客,记录日志,读写硬盘等场景,这些业务需要花大量的时间进行等待,而 CPU 此时任务量小,合理利用CPU资源,也能提高效率!!

    三、创建线程的5种方法

    💡1.继承 Thread 类,重写run()方法.

    class MyThread extends Thread {
        @Override
        public void run() {
            while (true) {
                System.out.println("继承 Thread 类");
            }
        }
    }
    public class Test {
        public static void main(String[] args) {
            Thread thread = new MyThread();
            thread.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    重写 run() 方法创建出了一个线程对象,但此时线程对象并没有运行起来,相当于运动员已准备就绪. 此时需要调用 start() 方法,打出发令枪,线程才真正执行起来. 虽然调用 run() 方法也能得到相同结果,但系统内核并没有创建线程对象.

    💡2.实现 Runnable 接口,重写run()方法.

    class MyThread1 implements Runnable {
        @Override
        public void run() {
            while (true) {
                System.out.println("实现 Runnable 接口");
            }
        }
    }
    public class Test1 {
        public static void main(String[] args) {
            Thread thread = new Thread(new MyThread1());
            thread.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    使用 jconsole 命令观察线程执行状态,
    路径地址C:\ProgramFiles\Java\jdk1.8.0_191\bin\jconsole.exe.

    名称: Thread-0
    状态: TIMED_WAITING
    总阻止数: 0, 总等待数: 32
    堆栈跟踪:
    java.lang.Thread.sleep(Native Method)
    MyThread1.run(Thread2.java:14)
    java.lang.Thread.run(Thread.java:748)

    💡3. 匿名内部类创建 Thread 子类对象.

    public class Test2 {
        public static void main(String[] args) {
            Thread thread = new Thread() {
                @Override
                public void run() {
                    System.out.println("匿名内部类创建 Thread 子类对象");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            thread.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    休眠当前线程,可以调用 sleep() 方法,因为线程是随机调度的,实际休眠时间是大于等于设置的休眠时间的,也就是说,线程可能休眠完毕不会立即执行的.

    💡4. 匿名内部类创建 Runnable 子类对象.

    public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!Thread.currentThread().isInterrupted()) {
                        System.out.println(" 匿名内部类创建 Runnable 子类对象");
                    }
                    System.out.println("线程结束");
                }
            });
            thread.start();
            Thread.sleep(3000);
            thread.interrupt();
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    😋😋中断线程有两种方式,可以自己设定标记位来判断是否暂停当前线程,也可以调用 interrupt() 方法来通知.

    1.如果thread线程没有处在阻塞状态,interrupt就会修改标记位.
    2.如果thread线程处于阻塞状态, interrupt就会抛出异常信息,我们可以处理完毕任务之后,再跳出循环.

    📢📢📢注意: 使用 interrupted() 方法判断当前线程的标志位是否设置,调用后清除标志位. 使用 isInterrupted() 方法调用后不清除标志位.

    💡5. Lambda 表达式,理解困难,但是代码量是最少的.

    public class Test4 {
        public static void main(String[] args) {
            Thread thread = new Thread(() -> {
                System.out.println("lambda 表达式");
            });
            System.out.println(thread.getState());
            thread.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    查看线程当前的状态,可以调用 getState() 方法,线程的状态一般分为:NEW 、 RUNNABLE 、 TERMINATED 、WAITING 、READY(就绪)、 BLOCKED 、 TIMED_WAITING几种状态.
    🔽🔽🔽

    NEW: 创建好Thread对象,但系统里并没有线程.
    RUNNABLE: 线程可能正在运行中,也可能没在CPU上运行.
    WAITING: 阻塞状态,线程中调用了 wait() 方法.
    BLOCKED: 阻塞状态,线程中调用了 Synchronized() 方法.
    TIMED_WAITING: 阻塞状态,线程中调用了 sleep() 方法.
    TERMINATED: 线程已经执行完毕,系统里的线程已经销毁,但线程对象仍然存在.

    四、多线程的优势

    💡数值a和b分别自增20_0000_0000次,观察它们的执行时间.

    串行:

    //串行
        public static void serial() {
            long str = System.currentTimeMillis();
            int a = 0;
            for (int i = 0; i < count; i++) {
                a++;
            }
            int b = 0;
            for (int i = 0; i < count; i++) {
                b++;
            }
            long end = System.currentTimeMillis();
            System.out.println("时间为:"+(end-str));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    并发:

     public static void concurrency() {
            //并发
            long str = System.currentTimeMillis();
    
            Thread t1 = new Thread() {
                @Override
                public void run() {
                    int a = 0;
                    for (int i = 0; i < count; i++) {
                        a++;
                    }
                }
            };
            Thread t2 = new Thread() {
                @Override
                public void run() {
                    int b = 0;
                    for (int i = 0; i < count; i++) {
                        b++;
                    }
                }
            };
            t1.start();
            t2.start();
    
            try {
                t1.join();
                t2.join();
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            long end = System.currentTimeMillis();
            System.out.println("时间为:"+(end-str));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    等待一个线程,可以调用 join() 方法,考虑到线程是随机调度的,如果t1, t2 不执行完毕的话,直接调用currentTimeMillis() 方法,获取的时间是不合理的.
    结果:

    此时,可以看出多线程在解决 20 亿数据自增的时候,效率明显高于串行, 这里的运行结果也不绝对,是根据自己电脑的配置和系统后台的运行程序数量决定的.

  • 相关阅读:
    “客户端到服务器的数据传递”和“服务器上的数据传递”这两种数据传递的方式的区别
    2022-8-18 第七小组 学习日记 (day42)JDBC+练习题
    torch.multiprocesssing
    git企业级使用
    spring学习第二天_Spring Ioc(1)
    jvm组成及内存模型
    AI大模型重塑新媒体变现格局:智能写作技术助力腾飞!
    linux动静态库
    Agile Management
    大数据学习(4)-hive表操作
  • 原文地址:https://blog.csdn.net/qq_59854519/article/details/127733411