• Java多线程之Thread和Runnable多线程的简单实现(适合小白入门,十分简单)


    一、介绍

    1、程序

    程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

    2、进程

    执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位。

    3、线程

    通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程。线程是CPU调度和执行的单位

    4、整体思路

    (1)线程是就是独立的执行路径。
    (2)在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程。
    (3)main()称之为主线程,为系统的入口,用于执行整个程序。
    (4)在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序不能人为干预。
    (5)对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。
    (6)线程会带来额外的开销。
    (7)每个线程在自己工作内存交互,内存控制不当会造成数据不一致。

    5、注意

    很多的多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。

    二、Thread

    1、思路流程

    (1)自定义线程类继承Thread类
    (2)重写run( )方法,编写线程执行体
    (3)创建线程对象,调用start ( )方法启动线程
    (4)线程不一定立即执行,CPU安排调度

    2、样例代码

    package com.example.multithreading.demo1;
    
    // 创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
    
    // 总结:注意,线程开启不一定立即执行,由CPU调度执行
    public class ThreadTest1 extends Thread{
    
        @Override
        public void run(){
            // run方法线程体
            for (int i = 0; i < 10; i++) {
                System.out.println("线程" + i);
            }
        }
    
        public static void main(String[] args){
            // main线程,主线程
            // 创建一个线程对象
            ThreadTest1 threadTest1 = new ThreadTest1();
            // 调用start()方法开启线程
            threadTest1.start();
    
            for (int i = 0; i < 20; i++) {
                System.out.println("主线程");
            }
        }
    
    }
    
    
    • 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

    结果
    在这里插入图片描述

    3、多线程下载图片

    package com.example.multithreading.demo1;
    
    import org.apache.commons.io.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    // 实现多线程同步下载图片
    public class ThreadTest2 extends Thread{
    
        // 网络图片地址
        private String url;
        // 保存的文件名
        private String name;
    
        public ThreadTest2(String url, String name){
            this.url = url;
            this.name = name;
        }
    
        // 下载图片线程的执行体
        @Override
        public void run(){
            WebDownloader webDownloader = new WebDownloader();
            webDownloader.downloader(url, name);
            System.out.println("下载了文件名为:" + name);
    
        }
    
        public static void main(String[] args){
            ThreadTest2 threadTest1 = new ThreadTest2("http://www.pp3.cn/uploads/allimg/200710/14-200G00Z321.jpg","风景图1.jpg");
            ThreadTest2 threadTest2 = new ThreadTest2("http://www.pp3.cn/uploads/allimg/200710/14-200G00Z321.jpg","风景图2.jpg");
            ThreadTest2 threadTest3 = new ThreadTest2("http://www.pp3.cn/uploads/allimg/200710/14-200G00Z321.jpg","风景图3.jpg");
            threadTest1.start();
            threadTest2.start();
            threadTest3.start();
        }
    
    }
    
    // 下载器
    class WebDownloader{
        // 下载方法
        public void downloader(String url, String name){
            try{
                FileUtils.copyURLToFile(new URL(url), new File(name));
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
    
    • 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
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    结果
    在这里插入图片描述

    三、Runnable

    1、思路流程

    (1)定义一个类实现Runnable接口
    (2)实现run( )方法,编写线程执行体
    (3)创建线程对象,调用start( )方法启动线程
    (因为java单继承的局限性,推荐使用Runnable对象)

    2、样例代码

    package com.example.multithreading.demo1;
    
    // 创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
    public class ThreadTest3 implements Runnable{
        @Override
        public void run() {
            // run方法线程体
            for (int i = 0; i < 10; i++) {
                System.out.println("线程" + i);
            }
        }
    
        public static void main(String[] args){
            // 创建runnable接口的实现类对象
            ThreadTest3 threadTest3 = new ThreadTest3();
    
            // 创建线程对象,通过线程对象来开启线程代理
            new Thread(threadTest3).start();
    
            for (int i = 0; i < 20; i++) {
                System.out.println("主线程");
            }
        }
    }
    
    
    • 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

    结果
    在这里插入图片描述

    3、多线程下载图片

    package com.example.multithreading.demo1;
    
    import org.apache.commons.io.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    public class ThreadTest3_1 implements Runnable{
    
        // 网络图片地址
        private String url;
        // 保存的文件名
        private String name;
    
        public ThreadTest3_1(String url, String name){
            this.url = url;
            this.name = name;
        }
    
        // 下载图片线程的执行体
        @Override
        public void run(){
            WebDownloader2 webDownloader2 = new WebDownloader2();
            webDownloader2.downloader(url, name);
            System.out.println("下载了文件名为:" + name);
    
        }
    
        public static void main(String[] args){
            ThreadTest2 threadTest1 = new ThreadTest2("http://www.pp3.cn/uploads/allimg/200710/14-200G00Z321.jpg","风景图1.jpg");
            ThreadTest2 threadTest2 = new ThreadTest2("http://www.pp3.cn/uploads/allimg/200710/14-200G00Z321.jpg","风景图2.jpg");
            ThreadTest2 threadTest3 = new ThreadTest2("http://www.pp3.cn/uploads/allimg/200710/14-200G00Z321.jpg","风景图3.jpg");
            new Thread(threadTest1).start();
            new Thread(threadTest2).start();
            new Thread(threadTest3).start();
        }
    
    }
    
    
    // 下载器
    class WebDownloader2{
        // 下载方法
        public void downloader(String url, String name){
            try{
                FileUtils.copyURLToFile(new URL(url), new File(name));
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
    • 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
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    结果
    在这里插入图片描述

    四、Callable接口(仅作了解)

    1、具体流程

    (1)实现Callable接口,需要返回值类型
    (2)重写call方法,需要抛出异常
    (3)创建目标对像
    (4)创建执行服务:ExecutorService ser = Excecutors.newFixedThreadPool(1);
    (5)提交执行:Future result1 = ser.submit(t1);
    (6)获取结果:boolean r1 = result1.get()
    (7)关闭服务:ser.shutdownNow()

    2、优点

    可以定义返回值
    可以抛出异常

    3、样例

    package com.example.multithreading.demo2;
    
    import com.example.multithreading.demo1.ThreadTest2;
    import org.apache.commons.io.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.concurrent.*;
    
    public class CallableTest implements Callable<Boolean> {
    
        // 网络图片地址
        private String url;
        // 保存的文件名
        private String name;
    
        public CallableTest(String url, String name){
            this.url = url;
            this.name = name;
        }
    
        // 下载图片线程的执行体
        @Override
        public Boolean call(){
            WebDownloader3 webDownloader = new WebDownloader3();
            webDownloader.downloader(url, name);
            System.out.println("下载了文件名为:" + name);
            return true;
        }
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CallableTest t1 = new CallableTest("http://www.pp3.cn/uploads/allimg/200710/14-200G00Z321.jpg","风景图1.jpg");
            CallableTest t2 = new CallableTest("http://www.pp3.cn/uploads/allimg/200710/14-200G00Z321.jpg","风景图2.jpg");
            CallableTest t3 = new CallableTest("http://www.pp3.cn/uploads/allimg/200710/14-200G00Z321.jpg","风景图3.jpg");
    
            // 创建执行服务
            ExecutorService ser = Executors.newFixedThreadPool(3);
    
            // 提交执行
            Future<Boolean> r1 = ser.submit(t1);
            Future<Boolean> r2 = ser.submit(t2);
            Future<Boolean> r3 = ser.submit(t3);
    
            // 获取结果
            boolean rs1 = r1.get();
            boolean rs2 = r2.get();
            boolean rs3 = r3.get();
    
            System.out.println(rs1);
            System.out.println(rs2);
            System.out.println(rs3);
    
            // 关闭服务
            ser.shutdownNow();
        }
    }
    
    // 下载器
    class WebDownloader3{
        // 下载方法
        public void downloader(String url, String name){
            try{
                FileUtils.copyURLToFile(new URL(url), new File(name));
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
    • 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
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    结果
    在这里插入图片描述

    五、两者比较(Thread与Runnable)

    1、Thread类

    (1)子类继承Thread类具备多线程能力
    (2)启动线程:子类对象.start( )
    (不建议使用:避免OOP单继承局限性)

    ThreadTest1 threadTest1 = new ThreadTest1();
    threadTest1.start();
    
    • 1
    • 2

    2、Runnable接口

    (1)实现接口Runnable具有多线程能力
    (2)启动线程:传入目标对象 + Thread对象.start( )
    (推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用)

    // 一份资源
    ThreadTest threadTest = new ThreadTest();
    // 多个代理
    new Thread(threadTest, "张三").start();
    new Thread(threadTest, "李四").start();
    new Thread(threadTest, "王五").start();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3、并发问题

    3.1 问题场景

    多个线程操作同一个资源的情况下,线程不安全,数据会出现紊乱。

    3.2 样例

    package com.example.multithreading.demo1;
    
    // 多个线程同时操作同一个对像
    // 买票
    public class ThreadTest4 implements Runnable{
    
        private int num = 10;
    
        @Override
        public void run() {
            while (true) {
                if (num <= 0) {
                    break;
                }
    
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
    
                System.out.println(Thread.currentThread().getName() + "-->拿到了第" + num-- + "张票");
            }
        }
    
        public static void main(String[] args) {
            ThreadTest4 threadTest4= new ThreadTest4();
            new Thread(threadTest4, "张三").start();
            new Thread(threadTest4,"李四").start();
            new Thread(threadTest4,"王五").start();
        }
    }
    
    
    • 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

    结果
    在这里插入图片描述

    六、多线程模拟龟兔赛跑

    1、具体代码

    package com.example.multithreading.demo1;
    
    // 模拟龟兔赛跑
    public class TortoiseAndRabbitRace implements Runnable {
    
        // 定义胜利者
        private static String winner;
    
        @Override
        public void run() {
            for (int i = 0; i <= 100; i++) {
                // 模拟兔子休息
                if(Thread.currentThread().getName().equals("兔子") && i%10==0){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
    
                // 判断比赛是否结束
                boolean flag = gameOver(i);
    
                if (flag) {
                    break;
                }
    
                System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
            }
        }
    
        // 判断是否完成比赛
        private boolean gameOver(int steps) {
            // 判断是否有胜利者
            if(winner != null){
                // 已经存在胜利者
                return true;
            }
            if (steps >= 100){
                winner = Thread.currentThread().getName();
                System.out.println("winner is " + winner);
                return true;
            }
            return false;
        }
    
        public static void main(String[] args) {
            TortoiseAndRabbitRace race = new TortoiseAndRabbitRace();
            new Thread(race,"兔子").start();
            new Thread(race,"乌龟").start();
        }
    }
    
    
    • 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
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    结果
    在这里插入图片描述

  • 相关阅读:
    【第48篇】MaxViT:多轴视觉转换器
    深度学习基础汇总
    Keras CIFAR-10分类 自定义simple CNN篇
    RocketMQ生产环境常见问题分析与总结
    解决项目下的lib包没有打进jar的问题
    API安全的应用和分析
    深入解析Linux中的用户态与内核态
    C++面向对象基础:访问权限、三种继承权限详解
    初识二叉搜索树
    外汇天眼:全员免费,赢奖金!
  • 原文地址:https://blog.csdn.net/qq_46106857/article/details/128131798