• Java多线程


    一、线程简介

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

    二、线程创建

    • Thread class(继承Thread类)<重点>
    • Runnable 类(实现Runnable接口)<重点>
    • Callable 类(实现Callable接口)<了解>
    创建线程方式一:继承Thread类
    1. 自定义线程类继承Thread类
    2. 重写run()方法,编写线程执行体
    3. 创建线程对象,调用start()方法启动线程
    //创建线程方式一:继承Thread类,重写run()方法,调用start()开启线程
    //总结:注意,线程开启不一定立即执行,由cpu调度执行
    public class TestThread1 extends Thread {
       
        @Override
        public void run() {
       
            //run方法线程体
            for (int i = 0; i < 200; i++) {
       
                System.out.println("我在看代码---" + i);
            }
        }
    
        public static void main(String[] args) {
       
            //main线程,主线程
    
            //创建一个线程对象
            TestThread1 testThread1 = new TestThread1();
            //调用start()方法开启线程
            testThread1.start();
    
            for (int i = 0; i < 1000; i++) {
       
                System.out.println("我在学习多线程---" + i);
            }
        }
    }
    
    • 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

    练习:实现多线程同步下载图片

    //练习Thread,实现多线程同步下载图片
    public class TestThread2 extends Thread {
       
    
        private String url; //网络图片地址
        private String name; //保存的文件名
    
        public TestThread2(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) {
       
            TestThread2 t1 = new TestThread2("https://img2.baidu.com/it/u=3078808011,1639218045&fm=253&fmt=auto&app=138&f=JPEG?w=333&h=500", "lufei1.jpg");
            TestThread2 t2 = new TestThread2("https://img1.baidu.com/it/u=1394802070,2956497274&fm=253&fmt=auto&app=138&f=JPEG?w=617&h=500", "lufei2.jpg");
            TestThread2 t3 = new TestThread2("https://img2.baidu.com/it/u=3684004695,1686754267&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=533", "lufei3.jpg");
    
            //同时执行
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    //下载器
    class WebDownloader {
       
        //下载方法
        public void downloader(String url, String name) {
       
            try {
       
                FileUtils.copyURLToFile(new URL(url), new File(name));
            } catch (IOException e) {
       
                e.printStackTrace();
                System.out.println("IO异常,downloader方法出现问题");
            }
        }
    }
    
    • 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

    创建线程方式二:实现Runnable接口
    1. 定义MyRunnable类实现Runnable接口
    2. 实现run()方法,编写线程执行体
    3. 创建线程对象,调用start()方法启动线程
    //创建线程方式二:实现Runnable接口,重写run()方法,执行线程需要丢入Runnable接口实现类,调用start()方法
    public class TestThread3 implements Runnable {
       
        @Override
        public void run() {
       
            //run方法线程体
            for (int i = 0; i < 200; i++) {
       
                System.out.println("我在看代码---" + i);
            }
        }
    
        public static void main(String[] args) {
       
            //创建runnable接口的实现类对象
            TestThread3 testThread3 = new TestThread3();
    
            //创建线程对象,通过线程对象来开启我们的线程,代理
            new Thread(testThread3).start();
    
            for (int i = 0; i < 1000; i++) {
       
                System.out.println("我在学习多线程---" + i);
            }
        }
    }
    
    • 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

    小结
    • 创建线程方式一:继承Thread类
      • 子类继承Thread类,具备多线程能力
      • 启动线程:子类对象.start()
      • 不建议使用:避免OOP单继承局限性
    • 创建线程方式二:实现Runnable接口
      • 实现接口Runnable,具有多线程能力
      • 启动线程:传入目标对象 + Thread对象.start()
      • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

    初识并发问题
    //多个线程同时操作同一个对象
    //买火车票的例子
    //发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱。
    public class TestThread4 implements Runnable {
       
    
        //票数
        private int ticketNums = 10;
    
        @Override
        public void run() {
       
            while (true) {
       
                if (ticketNums <= 0) {
       
                    break;
                }
                //模拟延时
    //            try {
       
    //                Thread.sleep(200);
    //            } catch (InterruptedException e) {
       
    //                e.printStackTrace();
    //            }
    
                System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
            }
        }
    
        public static void main(String[] args) {
       
    
            TestThread4 ticket = new TestThread4();
    
            new Thread(ticket, "小明").start();
            new Thread(ticket, "老师").start();
            new Thread(ticket, "黄牛党").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

    龟兔赛跑

    //模拟龟兔赛跑
    public class Race implements Runnable {
       
    
        //胜利者
        private static String winner;
    
        @Override
        public void run() {
       
            for (int i = 1; i <= 100; i++) {
       
    
                //模拟兔子休息
                if (Thread.currentThread().getName().equals("兔子") && (i % 10 == 0)) {
       
                    try {
       
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
       
                        e.printStackTrace();
                    }
                }
    
                //判断比赛是否结束
                boolean flag = gameOver(i);
                //如果比赛结束了,就停止程序
                if (flag) {
       
                    break;
                }
                System.out.println(Thread.currentThread().getName() + " --> 跑了" + i + "米");
            }
        }
    
        //判断是否完成比赛
        private boolean gameOver(int steps) {
       
            //判断是否有胜利者
            if (winner != null) {
        //已经存在胜利者了
                return true;
            }else {
       
                if (steps >= 100) {
       
                    winner = Thread.currentThread().getName();
                    System.out.println("winner is " + winner);
                    return true;
                }
            }
            return false;
        }
    
        public static void main(String[] args) {
       
            Race race = new Race();
    
            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
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

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

    利用Callable改造下载图片案例

    //线程创建方式三:实现Callable接口
    /*
    Callable的好处:
        1.可以定义返回值
        2.可以抛出异常
     */
    public class TestCallable implements Callable<Boolean> {
       
    
        private String url; //网络图片地址
        private String name; //保存的文件名
    
        public TestCallable(String url, String name) {
       
            this.url = url;
            this.name = name;
        }
    
        //下载图片线程的执行体
        @Override
        public Boolean call() throws Exception {
       
            WebDownloader webDownloader = new WebDownloader();
            webDownloader.downloader(url, name);
            System.out.println("已下载文件:" + name);
            return true;
        }
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
       
            TestCallable t1 = new TestCallable("https://img2.baidu.com/it/u=3078808011,1639218045&fm=253&fmt=auto&app=138&f=JPEG?w=333&h=500", "lufei1.jpg");
            TestCallable t2 = new TestCallable("https://img1.baidu.com/it/u=1394802070,2956497274&fm=253&fmt=auto&app=138&f=JPEG?w=617&h=500", "lufei2.jpg");
            TestCallable t3 = new TestCallable("https://img2.baidu.com/it/u=3684004695,1686754267&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=533", "lufei3.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 WebDownloader {
       
        //下载方法
        public void downloader(String url, String name) {
       
            try {
       
                FileUtils.copyURLToFile(new URL(url), new File(name));
            } catch (IOException e) {
       
                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
  • 相关阅读:
    论文笔记:SAITS: SELF-ATTENTION-BASED IMPUTATION FOR TIMESERIES
    linux中常用shell脚本整理
    深入理解跳表及其在Redis中的应用
    Vue技术-mapstart与mapGetters=》模块化+命名空间
    [Servlet 1] JSP基础知识
    忘记mysql密码后如何修改密码(2022最新版详细教程保姆级)
    springcloud-Eureka
    idea中完美解决Error:java: Compilation failed: internal java compiler error的问题
    计算机毕业设计ssm基于JAVAEE的车检预约系统846ks系统+程序+源码+lw+远程部署
    探索电子元器件商城:从原型到批量生产的选择
  • 原文地址:https://blog.csdn.net/m0_61467488/article/details/126650809