Java之多线程全解析

线程简介

多任务

  • 现实中太多这样同时做多件事情的例子了,看起来是多个任务都在做,其实本质上我们的大脑在同一时间依旧只做了一件事情

普通方法调用和多线程

在这里插入图片描述

Process 与 Thread

  • 说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
  • 而进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位
  • 通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位
  • 注意:很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器。如果是模拟出来的多线程,即在一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错局。

本章核心概念

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

线程创建

  • 三种创建方式
    在这里插入图片描述

继承 Thread 类

  • 自定义线程类继承 Thread 类

  • 重写run()方法,编写线程执行体

  • 创建线程对象,调用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) {
         
    //        mian线程 ,主线程
    
            //创建一个线程对象
            TestThread1 testThread1 = new TestThread1();
    
            //调用start()方法开启线程
            testThread1.start();
    
            for (int i = 0; i < 2000; i++) {
         
                System.out.println("我在学习多线程"+i);
            }
    
        }
    }
    

网图下载

  • //练习Thread , 实现多线程同步下载图片
    public class TsetThread2 extends Thread {
         
    
        private String url;//网络图片地址
        private String name;//保存的文件名
    
        public TsetThread2(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) {
         
            TsetThread2 t1 = new TsetThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2109242332225H9-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1642259534&t=b8168f36d5080fe91ca78d5ba0b42579", "1.jpg");
            TsetThread2 t2 = new TsetThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.redocn.com%2Fphoto%2F20140313%2FRedocn_2014031216224618.jpg&refer=http%3A%2F%2Fimg.redocn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1642259534&t=1aa3eaa4a74edd45dc45ad3724da3678", "2.jpg");
            TsetThread2 t3 = new TsetThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2Ftp05%2F19100122420C335-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1642259534&t=5b6dd1a688e8c826489f2d4f20c2cba6", "3.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方法出现问题");
           }
        }
    }
    

实现 Runnable 接口

  • //创建线程方式2 : 实现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();
            //创建线程对象 ,通过线程对象来开启我们的线程 , 代理
            Thread thread = new Thread(testThread3);
    
            thread.start();
    
    
            for (int i = 0; i < 2000; i++) {
                System.out.println("我在学习多线程"+i);
            }
    
        }
    }
    

小结

  • 继承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();
    
    
    
        }
    
    }
    

案例 龟兔赛跑

  • //模拟龟兔赛跑
    public class Race 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(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;
            }{
         
                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();
        }
    }
    

实现Callable 接口

  • 实现Callable 接口,需要返回值类型

  • 重写call方法,需要抛出异常

  • 创建目标对象

  • 执行创建服务:ExecutorService ser = Executors.newFixedThreadPool(3);

  • 提交执行:Future r1 = ser.submit(t1);

  • 获取结果:boolean rs1 = r1.get();

  • 关闭服务:ser.shutdown();

  • /**
     *线程创建方式三:实现callable接口
     *callable 的好处:
     * 可以定义返回值
     * 可以抛出异常
     */
    
    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() {
         
           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://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2109242332225H9-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1642259534&t=b8168f36d5080fe91ca78d5ba0b42579", "1.jpg");
            TestCallable t2 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.redocn.com%2Fphoto%2F20140313%2FRedocn_2014031216224618.jpg&refer=http%3A%2F%2Fimg.redocn.com&app=2002&size=
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值