黑白客 发表于 2021-2-26 15:33

多线程实现,并发,静态代{过}{滤}理以及lambda表达式的推演知识详解

本帖最后由 黑白客 于 2021-2-26 15:36 编辑


java多线程
线程简介
线程实现
初识并发问题
实现Callable接口
静态代{过}{滤}理
Lambda表达式

java多线程

## 线程简介

多线程:比如我们可以在电脑上一边听音乐,一边浏览网页等等。

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

## 线程实现

- 方法一
- 创建一个类,继承Thread类。实现其run()方法、run方法就是线程体
- 在main方法中创建类的对象,调用start()方法。实现多线程。如果调用run方法,就不会实现多线程。是单线程。
- 线程创建之后并不会立即执行,需要cpu的调度安排。cpu在同一时间只能调用一个线程。

网图下载:利用多线程,下载多个网图

```java
package com.wang.threadDemo;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
* @author: 王海新
* @Date: 2021/2/22 11:24
* @Description:下载网图
*/
public class TestThread2 extends Thread {
    private String url;
    private String name;

    TestThread2(String url,String name){
      this.url = url;
      this.name = name;
    }
    @Override
    public void run() {
      WebDownloader webDownloader = new WebDownloader();
      webDownloader.download(url,name);
      System.out.println("文件被下载了" + name);
    }
    public static void main(String[] args) {
      TestThread2 test1 = new TestThread2("https://ss1.baidu.com/9vo3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/cdbf6c81800a19d86524639732fa828ba61e4679.jpg", "1.jpg");
      TestThread2 test2 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.08087.cc%2Fuploads%2F20191221%2F19%2F1576927577-ezdtLhXcng.jpg&refer=http%3A%2F%2Fimg.08087.cc&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1616557755&t=9c5d0cdc0fd6d2988b9522d0dab2f9c7", "2.jpg");
      TestThread2 test3 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.08087.cc%2Fuploads%2F20190819%2F10%2F1566182328-nwMPsxzGXL.jpg&refer=http%3A%2F%2Fimg.08087.cc&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1616557823&t=e0da3b689853d27b1a26d2879faec7eb", "3.jpg");

      test1.start();
      test2.start();
      test3.start();

    }
}

/*********************************************************************************************************************
* @Author:王海新
* @Date:11:262021/2/22
* @Version:1.0.0
* @Description:下载器
*/
classWebDownloader{
    //下载方法
    public void download(String url,String name){
      try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
      } catch (IOException e) {
            System.out.println("io出错,出错方法:WebDownload");
      }
    }
}
```

- 第二种方法:实现Runable接口,推荐使用 避免单线程的局限性。

```java
package com.wang.threadDemo;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
* @author: 王海新
* @Date: 2021/2/22 16:36
* @Description: 实现Runnable接口
*
*/
public class TestThread3 implements Runnable {
    private String url;
    private String name;

    TestThread3(String url,String name){
      this.url = url;
      this.name = name;
    }
    @Override
    public void run() {
      WebDownloader1 webDownloader = new WebDownloader1();
      webDownloader.download1(url,name);
      System.out.println("文件被下载了" + name);
    }
    public static void main(String[] args) {



      TestThread3 test1 = new TestThread3(
                "https://ss1.baidu.com/9vo3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/cdbf6c81800a19d86524639732fa828ba61e4679.jpg", "4.jpg");
      TestThread3 test2 = new TestThread3(
                "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.08087.cc%2Fuploads%2F20191221%2F19%2F1576927577-ezdtLhXcng.jpg&refer=http%3A%2F%2Fimg.08087.cc&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1616557755&t=9c5d0cdc0fd6d2988b9522d0dab2f9c7", "5.jpg");
      TestThread3 test3 = new TestThread3(
                "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.08087.cc%2Fuploads%2F20190819%2F10%2F1566182328-nwMPsxzGXL.jpg&refer=http%3A%2F%2Fimg.08087.cc&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1616557823&t=e0da3b689853d27b1a26d2879faec7eb", "6.jpg");

      new Thread(test1).start();
      new Thread(test2).start();
      new Thread(test3).start();

    }
}

/*********************************************************************************************************************
* @Author:王海新
* @Date:11:262021/2/22
* @Version:1.0.0
* @Description:下载器
*/
classWebDownloader1 {
    //下载方法
    public void download1(String url, String name) {

      try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
      } catch (IOException e) {
            e.printStackTrace();
            System.out.println("io出错,出错方法:WebDownload1");
      }
    }
}
```

基本上和继承Thread类相同,只是在调用的时候。

new 实现类

new Thread(实现类名).start();来调用

## 初识并发问题

通过以下程序,可以发现在抢票的过程中,多个线程操作

同一个资源,会出现错误

```java
package com.wang.threadDemo;

/**
* @author: 王海新
* @Date: 2021/2/22 17:07
* @Description: 多个线程同时操作一个对象
* 买火车票的例子
*
* 发现问题:多个线程操作同一个资源的情况下,不安全
*/
public class TestThread4 implements Runnable{
    private int ticketNumbel = 10; //火车票
    @Override
    public void run() {
      while (true){
            if (ticketNumbel <= 0) {
                break;
            }
            //添加延迟
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 拿到了第" + ticketNumbel-- + "张票");
      }
    }

    public static void main(String[] args) {
      TestThread4 ticket = new TestThread4();

      new Thread(ticket,"小明").start();
      new Thread(ticket,"老师").start();
      new Thread(ticket,"黄牛党").start();
      new Thread(ticket,"键盘侠").start();
    }
}
```

利用以下龟兔赛跑的案例,来巩固一下多线程的概念

```java
package com.wang.threadDemo;

/**
* @author: 王海新
* @Date: 2021/2/22 17:26
* @Description: 龟兔赛跑
*
*/
public class Race implements Runnable {

    privatestatic String winner;//胜利者
    @Override
    public void run() {
      for (int i = 1; i <= 100; i++) {
            //模拟兔子睡觉
            if (Thread.currentThread().getName().equals("兔子") && i == 90) {
                try {
                  Thread.sleep(200);
                } catch (InterruptedException e) {
                  e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "--> 跑了 " + i +"步");
            //判断是否跑完一百步
            if (gameOver(i)) {
                break;
            }
      }
    }
/*********************************************************************************************************************
* @Author:王海新
* @Date:17:342021/2/22
* @Version:1.0.0
* @Description:判断是否结束比赛
*/
    private boolean gameOver(int steps){
      //判断是否存在胜利者
      if (winner != null) {//已经存在获胜者
            return true;
      }
      if (steps >= 100) {
            winner = Thread.currentThread().getName();
            System.out.println( winner + "获胜");
            return true;
      }
      return false;
    }

    public static void main(String[] args) {
      Race race = new Race();

      new Thread(race,"乌龟").start();
      new Thread(race,"兔子").start();
    }
}
```

## 实现Callable接口

```java
package com.wang.threadDemo;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

/**
* @author: 王海新
* @Date: 2021/2/25 19:18
* @Description:线程创建方式三:实现callable接口
* 可以获取一个返回值
* 可以抛出异常
* 但是需要创建一个服务,通过服务提交
*/
public class TestCallable implements Callable<Boolean> {
    private String url;
    private String name;

    TestCallable(String url,String name){
      this.url = url;
      this.name = name;
    }
    @Override
    public Boolean call() {
      WebDownloader3 webDownloader = new WebDownloader3();
      webDownloader.download1(url,name);
      System.out.println("文件被下载了" + name);
      return true;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {

      TestCallable test1 = new TestCallable(
                "https://ss1.baidu.com/9vo3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/cdbf6c81800a19d86524639732fa828ba61e4679.jpg", "4.jpg");
      TestCallable test2 = new TestCallable(
                "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.08087.cc%2Fuploads%2F20191221%2F19%2F1576927577-ezdtLhXcng.jpg&refer=http%3A%2F%2Fimg.08087.cc&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1616557755&t=9c5d0cdc0fd6d2988b9522d0dab2f9c7", "5.jpg");
      TestCallable test3 = new TestCallable(
                "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.08087.cc%2Fuploads%2F20190819%2F10%2F1566182328-nwMPsxzGXL.jpg&refer=http%3A%2F%2Fimg.08087.cc&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1616557823&t=e0da3b689853d27b1a26d2879faec7eb", "6.jpg");

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

      //2 提交执行
      Future<Boolean> s1 = ser.submit(test1);
      Future<Boolean> s2 = ser.submit(test2);
      Future<Boolean> s3 = ser.submit(test3);

      //3 获取结果
      boolean q = s1.get();
      boolean q1 = s2.get();
      boolean q2 = s3.get();

      //4 关闭服务
      ser.shutdown();
    }
}


/*********************************************************************************************************************
* @Author:王海新
* @Date:19:212021/2/25
* @Version:1.0.0
* @Description:下载器
*/
classWebDownloader3 {
    //下载方法
    public void download1(String url, String name) {

      try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
      } catch (IOException e) {
            e.printStackTrace();
            System.out.println("io出错,出错方法:WebDownload1");
      }
    }
}
```



## 静态代{过}{滤}理

看一个结婚的案例

```java
package com.wang.threadDemo1;

/**
* @author: 王海新
* @Date: 2021/2/26 09:49
* @Description: 静态代{过}{滤}理模式总结
* 真实对象和代{过}{滤}理对象都要实现同一个接口
* 代{过}{滤}理对象要代{过}{滤}理真实角色
*
* 好处:
*代{过}{滤}理对象可以做很多真实对象做不了的事情
*真实对象专注做自己的事情
*/
public class StacticProxy {
    public static void main(String[] args) {
      WeddingCompany weddingCompany = new WeddingCompany(new You());
      weddingCompany.HappyMarry();
      //还可以简写为一行代码
      // new WeddingCompany(new You()).HappyMarry();
    }
}

//结婚的接口
interface Marry{
    /*********************************************************************************************************************
   * @Author:王海新
   * @Date:9:532021/2/26
   * @Version:1.0.0
   * @Description: 开心的结婚
   *   人间四大喜事
   *   * 久旱逢甘露
   *   * 他乡遇故知
   *   * 洞房花烛夜
   *   * 金榜题名时
   */
    void HappyMarry();
}

/*********************************************************************************************************************
* @Author:王海新
* @Date:9:542021/2/26
* @Version:1.0.0
* @Description:真实角色,你去结婚
*/
class You implements Marry{
    @Override
    public void HappyMarry() {
      System.out.println("我要结婚啦");
    }
}
/*********************************************************************************************************************
* @Author:王海新
* @Date:9:552021/2/26
* @Version:1.0.0
* @Description:代{过}{滤}理角色,帮助你结婚
* 他会在你原来实现功能之前之后添加新的东西
*/
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 after() {
      System.out.println("收尾款");
    }

    private void before() {
      System.out.println("结婚前,布置现场");
    }
}
```

## Lambda表达式

理解函数式接口是学习Lamda表达式的关键所在

- Lamda函数式接口的定义:
- 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
- 对于函数式接口,就可以通过lambda表达式来创建接口
- lambda推演过程

```java
package com.wang.threadDemo1;



/**
* @author: 王海新
* @Date: 2021/2/26 11:00
* @Description: 推演lambda表达式
*/
public class lambda {
    //2.定义一个静态内部类,实现
    static class Like2 implements Ilike{
      @Override
      public void lambda() {
            System.out.println(2);
      }
    }

    public static void main(String[] args) {
      //1.创建外部类对象实现
      Ilike like = new Like();
      like.lambda();

      Like2 likeq = new Like2();
      likeq.lambda();

      //3.定义一个局部内部类实现
      class Like3 implements Ilike{
            @Override
            public void lambda() {
                System.out.println(3);
            }
      }
      Like3 like1 = new Like3();
      like1.lambda();

      //4.匿名内部类,没有类的名称,必须借助类的接口或者父类
      like = new Ilike(){
            @Override
            public void lambda() {
                System.out.println(4);
            }
      };
      like.lambda();

      //5.使用lambda简化
      like = () -> {
            System.out.println("5");
      };
      like.lambda();
    }

}

//定义一个函数式接口
interface Ilike{
    void lambda();
}

//定义一个实现类
class Like implements Ilike{

    @Override
    public void lambda() {
      System.out.println(1);
    }
}
```

- lambda表达式简化

```java
package com.wang.threadDemo1.lambda;

/**
* @author: 王海新
* @Date: 2021/2/26 11:33
* @Description: 函数式接口的有参方法 简化
* 多个参数也可以去掉参数类型,但是要去掉都去掉。必须加上括号
*/
public class lambda2 {
    public static void main(String[] args) {
      //1
      Iove iove = (int a) ->{
            System.out.println("爱你"+a);
      };
      iove.honey(111);

      //2 去掉形参的参数类型
      Iove iove1 = (a) ->{
            System.out.println("爱你"+a);
      };
      iove1.honey(222);

      //3 去掉括号(无参方法不可以去掉
      Iove iove2 = a ->{
            System.out.println("爱你"+a);
      };
      iove2.honey(333);

      //4 去掉大括号(假如你的代码有多行,就不可以去掉大括号,因为一个分号就表示结束了。没办法添加新的一行
      Iove iove3 = a ->System.out.println("爱你"+a);
      iove3.honey(444);
    }

}

interface Iove{
    void honey(int a);
}
```

aa361611002 发表于 2021-2-26 16:17

楼主头像好评

白衣国度 发表于 2021-2-26 16:53

皮卡丘很好看,真不错

JsFuck攻城狮 发表于 2021-2-26 17:43

有头像妹子的 种子吗 求分享

nj001 发表于 2021-2-26 23:07

java的函数式编程让人心累

黑白客 发表于 2021-2-27 11:52

nj001 发表于 2021-2-26 23:07
java的函数式编程让人心累

嗯?是吗?
页: [1]
查看完整版本: 多线程实现,并发,静态代{过}{滤}理以及lambda表达式的推演知识详解