• Java中的多线程编程


    上节课内容回顾

    进程, 进程调度, PCB结构,进程的虚拟空间地址,进程间的通信
    进程和线程之间的区别和联系:

    1. 进程包含线程!一个进程里面可以有一个线程也可以有多个线程
    2. 进程和线程都能解决并发编程问题的场景,但是进程在频繁创建和销毁中,开销更高,线程开销更低(线程比进程更轻量级)
    3. 进程是系统分配资源(内存,文件资源…)基本单位,线程是系统调度执行的基本单位(CPU)
    4. 进程之间是相互独立的,各自有各自的虚拟空间地址.同一个进程的内部的多个线程之间共用一个内存空间以及文件资源.一个进程挂了其他进程一般都没事,但是一个线程挂了可能把整个进程搞崩

    多线程仍然是最主流最常见的一种并发编程的方式

    Java中如何进行多线程编程;
    在Java标准库中,就提供了一个Thread类来表示操作线程
    Thread可以视为Java标准库提供的API
    创建好的Thread实例,其实和操作系统的线程是一一对应的关系,操作系统提供了一组关于线程的API(C语言风格)Java对于这组API进一步封装,就成了Thread类

    Thread类的基本用法

    第一种

    通过Thread类创建线程,写法有多种,其中最简单的就是创建子类继承Thread,并且重写run方法

    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("hello thread!");
        }
    }
    public class Main {
        public static void main(String[] args) {
            Thread thread = new MyThread();
            thread.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    run描述了每个线程内部要执行那些代码,每个线程都是并发执行的(各自执行各自的代码),因此就需要告诉这个线程你要执行什么代码
    start需要调用这个start方法,才是真正的在系统上创建了线程,才是真正的执行上面的run操作,在调佣出start方法之前,系统是没有创建出线程的

    线程之间是并发执行的,如果一个循环中不加任何的限制,这个循环转的速度非常快,导致打印的东西太多了,根本看不过来,就可以加上一个sleep操作,来强制让这个线程休眠一段时间

    在一个进程中至少有一个线程,在一个Java进程中也是至少有一个调用main方法的线程,自己创建的线程和自动创建的main线程就是并发执行的关系(宏观看起来是同时执行)

    使用场景:

    class MyThread extends Thread {
        @Override
        public void run() {
            while(true) {
                System.out.println("hello thread!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public class Main {
        public static void main(String[] args) {
            Thread thread = new MyThread();
            thread.start();
            while(true) {
                System.out.println("hello main!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    • 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

    现在两个线程,都是先打印一条就休眠1s,当1s时间到了之后,系统先唤醒谁那?(看起来这个顺序不是完全确定的)随机的,对于操作系统来说,内部对于线程之间的调度顺序,在宏观上认为是随机的(抢占式的)

    第二种

    创建一个类,实现Runable接口,再创建Runable实例传给Thread实例

    class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("hello thread!");
        }
    }
    public class Main {
        public static void main(String[] args) {
            Thread thread = new Thread(new MyRunnable());
            thread.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    第三种

    就是上面俩种写法的翻版,使用了匿名内部类

    public class Main {
        public static void main(String[] args) {
            Thread thread = new Thread() {
                @Override
                public void run() {
                    System.out.println("hello thread!");
                }
            };
            thread.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    创建了一个匿名内部类,继承自Thread类,同时重写run方法,同时new这个匿名内部类的实例

    第四种
    public class Main {
        public static void main(String[] args) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello thread!");
                }
            });
            thread.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    new Runable针对这个创建的匿名内部类,同时new出的Runable实例传给Thread 的构造方法

    通常认为Runable这种写法更好一点,能够能够做到让线程和线程执行的任务更好的解耦,写代码一般希望高内聚,低耦合

    第五种

    相当于第四种写法的延伸,使用lambda表达式,是使用了lambda代替了Runable而已

    public class Main {
        public static void main(String[] args) {
            Thread thread = new Thread(() -> {
                System.out.println("hello thread!");
            });
            thread.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    以上五种写法都很常见大家都应该掌握

  • 相关阅读:
    RSA概念详解及工具推荐大全 - lmn
    LeetCode算法二叉树—144. 二叉树的前序遍历
    编译原理—运行环境、局部存储分配、活动记录、全局栈式存储分配、非局部名字的访问、参数传递
    新一代前端应该使用的“开发套餐“
    关于网络连接的一些易忘点-HTTP/TCP/UDP
    分享:使用宝塔搭建属于自己的邮局系统
    Qt 关于窗口全屏显示与退出全屏的实现
    电脑扫描不到端口了怎么回事啊?
    B. Neko Performs Cat Furrier Transform
    下载量突破10亿,MinIO的开源启示录
  • 原文地址:https://blog.csdn.net/m0_63185171/article/details/126804140