zoukankan      html  css  js  c++  java
  • JAVA学习day08

    线程简介

    定义

    进程和线程

    • 进程是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位。
    • 通常一个进程中可以包含若干个线程,当然一个进程中至少有一个线程。线程是CPU调度和执行的单位。

    核心概念

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

    线程实现(重点)

    Thread(类)、Runnable(接口)、Callable(接口)
    Thread JAVA中文文档

    Thread

    学习提示:查看JDK帮助文档
    1、 自定义线程类继承Thread类
    2、 重写run()方法,编写线程执行体
    3、 创建线程对象,调用start()方法启动线程

    TestThread1

    public class TestThread1 extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println("我在看代码==="+i);
            }
        }
    
        public static void main(String[] args) {
            //main线程,主线程
    
            //创建一个线程对象
            TestThread1 testThread1 = new TestThread1();
    
            //调用start()方法开启线程
            testThread1.start();
    
            for (int i = 0; i < 20; i++) {
                System.out.println("我在学习多线程=="+i);
            }
        }
    }
    

    TestThread2

    用线程同时下载三张图片
    准备工作:

    • 下载commons-io
    • 将commons-io-x.xx.x.jar复制到src下当前包第一层中的Lib文件夹中(如项目位置为src.com.a.b,则复制到com下,与a同级),如没有Lib则创建
    • 右击commons,点击“Add as Library”
    //联系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://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp1.itc.cn%2Fq_70%2Fimages01%2F20210608%2F2de4b5a9f4db46ee83b1081dc557929e.jpeg&refer=http%3A%2F%2Fp1.itc.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641959062&t=8de25f251674ec4f39a7e041008d7e92","2_1.jpg");
            TestThread2 t2 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fgroup_topic%2Fl%2Fpublic%2Fp410237170.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641959062&t=5cf69539e5e84752f4dbceae67d7b5ad","2_2.jpg");
            TestThread2 t3 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fup.enterdesk.com%2Fedpic_source%2F53%2F0a%2Fda%2F530adad966630fce548cd408237ff200.jpg&refer=http%3A%2F%2Fup.enterdesk.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641197606&t=93b9d70fc80f91b48ce99893742d0558","2_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方法出现问题");
            }
        }
    }
    

    总结:注意线程开启不一定立即执行,由CPU调度执行

    runnable

    • 实现runnable接口,重写run()方法
    • 创建类实例,丢入thread中
    • 调用thread.start()方法

    TestThread3

    public class TestThread3 implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println("我在看代码==="+i);
            }
        }
    
        public static void main(String[] args) {
    
            TestThread3 testThread3 = new TestThread3();
    
            //创建线程对象,通过线程对象来开启线程,代理
            //Thread thread = new Thread(testThread3);
            //thread.start();
    
            new Thread(testThread3).start();
    
            for (int i = 0; i < 20; i++) {
                System.out.println("我在学习多线程=="+i);
            }
        }
    }
    

    小结

    • 继承Thread类

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

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

    并发问题

    TestThread4

    //多个线程同时操作一个对象
    //买火车票的例子
    
    //发现问题,多个线程操作同一个资源的情况下,线程不安全,数据紊乱
    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();
        }
    }
    
    

    测试结果:
    老师-->拿到了第10票
    小明-->拿到了第9票
    黄牛-->拿到了第10票
    小明-->拿到了第8票
    黄牛-->拿到了第6票
    老师-->拿到了第7票
    小明-->拿到了第5票
    黄牛-->拿到了第5票
    老师-->拿到了第4票
    小明-->拿到了第3票
    黄牛-->拿到了第2票
    老师-->拿到了第3票
    小明-->拿到了第1票
    老师-->拿到了第1票
    黄牛-->拿到了第1票

    多线程同时拿一个资源不安全,应上锁

    案例:龟兔赛跑

    规则

    1. 要有赛道距离,离终点越来越近
    2. 判断比赛是否结束
    3. 打印胜利者
    4. 龟兔赛跑开始
    5. 模拟兔子睡觉让乌龟赢
    6. 乌龟赢

    TurtleRabbitRace

    //模拟龟兔赛跑
    public class TurtleRabbitRace 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();
                    }
                }
    
    
                System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
    
                //判断比赛是否结束
                boolean flag = gameOver(i);
                //如果比赛结束,跳出
                if(flag){
                    break;
                }
    
    
            }
        }
    
        //是否完成比赛
        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) {
            TurtleRabbitRace race = new TurtleRabbitRace();
    
            new Thread(race,"兔子").start();
            new Thread(race,"乌龟").start();
        }
    }
    
    

    注:原来race可以重复使用,可以往Thread内传入同一个Runnable target,然后命名。

    Callable

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

    TestCallable

    //线程创建方式三:实现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() {
            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%2Fp1.itc.cn%2Fq_70%2Fimages01%2F20210608%2F2de4b5a9f4db46ee83b1081dc557929e.jpeg&refer=http%3A%2F%2Fp1.itc.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641959062&t=8de25f251674ec4f39a7e041008d7e92","2_1.jpg");
            TestCallable t2 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg3.doubanio.com%2Fview%2Fgroup_topic%2Fl%2Fpublic%2Fp410237170.jpg&refer=http%3A%2F%2Fimg3.doubanio.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641959062&t=5cf69539e5e84752f4dbceae67d7b5ad","2_2.jpg");
            TestCallable t3 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fup.enterdesk.com%2Fedpic_source%2F53%2F0a%2Fda%2F530adad966630fce548cd408237ff200.jpg&refer=http%3A%2F%2Fup.enterdesk.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641197606&t=93b9d70fc80f91b48ce99893742d0558","2_3.jpg");
    
            //4、 创建执行服务:
            ExecutorService ser = Executors.newFixedThreadPool(3);
            //5、 提交执行:
            Future<Boolean> r1=ser.submit(t1);
            Future<Boolean> r2=ser.submit(t2);
            Future<Boolean> r3=ser.submit(t3);
            //6、 获取结果:
            boolean rs1 = r1.get();
            boolean rs2 = r2.get();
            boolean rs3 = r3.get();
    
            System.out.println(rs1);
            System.out.println(rs2);
            System.out.println(rs3);
            //7、 关闭服务:
            ser.shutdownNow();
    
    
        }
    }
    
    //下载器
    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方法出现问题");
            }
        }
    }
    
    

    get()到的返回值的类型在继承接口时已定义,具体返回值在Call里已定义,此代码已写死返回值为true

    静态代理

    别人帮你做事情(调用方法),runnable调用线程时,往Thread内传Runnable Target的操作,就是代理。

    
    //静态代理模式总结
    //真实对象和代理对象都要实现同一个接口
    //代理对象要代理真实角色
    //好处:
        //代理对象可以做很多真实对象做不了的事情
        //真实对象专注做自己的事情
    public class StaticProxy {
        public static void main(String[] args) {
            //本来自己结婚,现在通过婚庆公司调用HappyMarry
            You you = new You();
            //you.HappyMarry();
    
            new Thread(()-> System.out.println("lambda")).start();
    
            WeddingCompany weddingCompany = new WeddingCompany(new You());
            weddingCompany.HappyMarry();
        }
    }
    interface Marry{
        void HappyMarry();
    }
    
    //真实角色,你去结婚
    class You implements Marry{
    
        @Override
        public void HappyMarry() {
            System.out.println("结婚");
        }
    }
    
    //代理角色,帮助你结婚
    class WeddingCompany implements Marry{
    
        //代理对象-->真实目标角色
        private Marry target;
    
        public WeddingCompany(Marry target) {
            this.target = target;
        }
    
        @Override
        public void HappyMarry() {
            before();
            this.target.HappyMarry();
            after();
        }
    
        private void before() {
            System.out.println("结婚之前,布置现场");
        }
    
        private void after() {
            System.out.println("结婚之后,收尾款");
        }
    }
    

    运行结果:
    lambda
    结婚之前,布置现场
    结婚
    结婚之后,收尾款

    lamda表达式

    • λ时希腊字母表中排序第11位的字母,英文名称Lambda

    • 避免匿名内部类定义过多

    • 实质属于函数式编程概念

      • (params) -> expression[表达式]
      • (params) -> statement[语句]
      • (params) -> {statements}
        a->System.out.println("i like lambda-->"+a)
        new Thread(()->System.out.println("多线程学习。。。")).start();
    • 为什么要使用lambda表达式

      • 避免匿名内部类定义过多
      • 可以让代码看起来更简洁
      • 去掉了一堆无意义代码,只留下核心部分
        函数式接口的定义:任何借口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口;对于函数式接口,可以通过lambda表达式来创建该接口的对象。

    TestLambda

    6步简化

    /*
    推导Lambda表达式
     */
    public class TestLambda1 {
    
        //3、静态内部类
        static class Like2 implements ILike{
            @Override
            public void lambda() {
                System.out.println("I like lamdba2");
            }
        }
    
        public static void main(String[] args) {
            ILike like = new Like();
            like.lambda();
    
            like = new Like2();
            like.lambda();
    
    
            //4、局部内部类
            class Like3 implements ILike{
                @Override
                public void lambda() {
                    System.out.println("I like lamdba3");
                }
            }
            like = new Like3();
            like.lambda();
    
            //5、匿名内部类,没有类的名称,必须借助接口或者父类
            like = new ILike(){
    
                @Override
                public void lambda() {
                    System.out.println("i like lambda4");
                }
            };
            like.lambda();
    
    
            //6、用lambda简化
            like = () -> {
                System.out.println("i like lambda5");
            };
            like.lambda();
        }
    }
    
    //1、定义一个函数式接口
    interface ILike{
        void lambda();
    }
    //2、实现类
    class Like implements ILike{
        @Override
        public void lambda() {
            System.out.println("I like lamdba");
        }
    }
    
    

    TestLambda2

    简化Lambda表达式

    
    public class TestLambda2 {
        public static void main(String[] args) {
    
            //1、Lambda表示简化
            ILove love = (int a) -> {
                    System.out.println("love->"+a);
                };
            //简化1:去掉参数类型
            love = (a) -> {
                System.out.println("love->"+a);
            };
    
            //简化2:简化括号
            love = a->{
                System.out.println("love->"+a);
            };
            //简化3:去掉花括号(因为代码只有一行)
            love = a-> System.out.println("love->"+a);
    
            //总结:
                //lambda表达式只能在由一行代码的情况下才能简化成为一行,如果有多行,就用代码块包裹。
                //前提是接口为函数式接口
                //多个参数也可以去掉参数类型,要去掉就都去掉
    
            love.love(520);
    
        }
    }
    
    interface ILove{
        void love(int a);
    }
    

    lambda将本来需要用类实现的的函数式接口变成几行代码即可实现。精简方便。

  • 相关阅读:
    HttpURLconnection的介绍
    HttpClient4.3教程 第三章 Http状态管理
    HttpClient4.3教程 第二章 连接管理
    HttpClient 4.3教程 第一章 基本概念
    HttpClient 4.3教程-前言
    数据结构
    数据结构
    HashMap底层源码剖析
    防止XSS 攻击集成springboot
    C——Network Saboteur (POJ2531)
  • 原文地址:https://www.cnblogs.com/ebym/p/15683123.html
Copyright © 2011-2022 走看看