多线程的创建,并发,静态代理,Lambda表达式

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

 

在操作系统中运行的程序就是进程(Process),如:QQ,播放器,游戏等等。

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

 

一个进程可以有多个线程(Thread),如视频中同时听到声音,看图像,看弹幕等等。线程是CPU调度和执行的单位

 

注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。

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

 

线程是独立的执行路径。

在程序运行时,即使自己没有创建线程,后台也会有多个线程,如主线程,gc线程。gc:垃圾回收机制。守护线程。

main()为主线程,是系统的入口。

在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的。

对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。

线程会带来额外的开销,如cpu调度时间,并发控制开销。

每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。

线程不一定立即执行,由cpu调度安排。

 

1.方式一 :继承Thread类

  步骤:自定义线程类继承Thread类

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

     创建线程对象,调用start()方法启动线程。

public class ThreadDemo extends Thread{ //1继承Thread类
    public static void main(String[] args) {
        ThreadDemo threadDemoo = new ThreadDemo(); //3.创建线程对象
        threadDemoo.start(); //调用start方法,开启该线程
        //主线程
        for (int i = 0;i<2000;i++){
            System.out.println("我在看弹幕"+i);
        }
    }

    @Override
    public void run() {
        //2.重写run方法。线程体
        for (int i = 0;i<2000;i++){
            System.out.println("我在听音乐。"+i);
        }

    }
}

 

练习:

实现多线程同步下载图片。

引入一个工具类:implementation "commons-io:commons-io:2.7"

为了使用里面的copyURLToFile(new URL(url),new File(name));  ------>将一个网络地址的内容变成文件。

步骤:

  (1)创建一个下载器类 WebDownLoader

  (2)创建一个多线程类,继承Thread。

代码如下:

 

package com.java.multithreading;

import org.apache.commons.io.FileUtils;

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

//练习thread,实现多线程同步下载图片
public class TestThread  extends Thread{
    private String url;
    private String name;

    //构造器
    public TestThread(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) {
        TestThread testThread01 = new TestThread("https://img2.baidu.com/it/u=450745774,808114680&fm=253&fmt=auto&app=138&f=JPEG?w=370&h=500","xixi01.jpg");
        TestThread testThread02 = new TestThread("https://img0.baidu.com/it/u=1145082949,2832027117&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=914","xix02.jpg");
        TestThread testThread03 = new TestThread("http://t14.baidu.com/it/u=2571612918,2603191067&fm=224&app=112&f=JPEG?w=500&h=333","xixi03.jpg");

        testThread01.start();
        testThread02.start();
        testThread03.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方法出现问题");
        }
    }
}

 

运行结果如下:

 

 线程是同步执行的,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的。

Thread类是实现了Runnable接口的,我们继承Thread类,其实也就是实现了Runnable接口,然后重写了run方法。

 

 

 通过new ThreadDemo().start();开启多线程, 它执行了Thread的无参构造方法

    

2.方式二:实现Runnable接口

 

  步骤:自定义线程类实现Runnable接口

 

     实现run()方法,编写线程执行体

 

     创建线程对象,调用start()方法启动线程。

 

package com.java.multithreading;

public class RunnableDemo implements Runnable{
    @Override
    public void run() {
        //线程体
        for (int i = 0;i<200;i++){
            System.out.println("我在听音乐。"+i);
        }

    }

    public static void main(String[] args) {
        RunnableDemo runnableDemo = new RunnableDemo(); //创建Runnable接口的实现类对象
        Thread thread = new Thread(runnableDemo); //创建线程对象,通过线程对象来开启我们的线程
        thread.start(); //开启线程

        //可以将上面代码简写为:
        new Thread(runnableDemo).start();

       ; //调用start方法,开启该线程
        //主线程
        for (int i = 0;i<1000;i++){
            System.out.println("我在看弹幕"+i);
        }
    }
}

练习:

实现多线程同步下载图片。

此处只需将方式一的代码修改两步:

  将继承Thread类改为实现Runnable接口。

  将new ThreadDemo().start改为RunnableDemo runnableDemo = new RunnableDemo();  new Thread(runnableDemo).start();

引入一个工具类:implementation "commons-io:commons-io:2.7"

为了使用里面的copyURLToFile(new URL(url),new File(name));  ------>将一个网络地址的内容变成文件。

步骤:

  (1)创建一个下载器类 WebDownLoader。这里直接用方式一的下载器。

  (2)创建一个多线程类,实现Runnable接口。

代码如下:

 

package com.java.multithreading;

import org.apache.commons.io.FileUtils;

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

public class TestRunnable implements Runnable{
    private String url;
    private String name;

    //构造器
    public TestRunnable(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) {
TestRunnable testRunnable01 = new TestRunnable("https://img2.baidu.com/it/u=450745774,808114680&fm=253&fmt=auto&app=138&f=JPEG?w=370&h=500","xixi01.jpg");
TestRunnable testRunnable02 = new TestRunnable("https://img0.baidu.com/it/u=1145082949,2832027117&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=914","xix02.jpg");
TestRunnable testRunnable03 = new TestRunnable("http://t14.baidu.com/it/u=2571612918,2603191067&fm=224&app=112&f=JPEG?w=500&h=333","xixi03.jpg");

// testThread01.start();
// testThread02.start();
// testThread03.start();
new Thread(testRunnable01).start();
new Thread(testRunnable02).start();
new Thread(testRunnable03).start();
}
}

 

结果如下:

两者比较:

继承Thread类:

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

实现Runnable接口:

  • 实现接口Runnable具有多线程能力
  • 启动线程:new Thread(Runnable接口的实现类对象).start()
  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。

 

 

3.方式三:实现Callable接口

  步骤:

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

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

    创建目标对象 t1

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

    提交执行:Future<Boolean> result = ser.submit(t1);

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

    关闭服务:ser.shutdownNow();

 

package com.java.multithreading;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CallableDemo implements Callable<Boolean>{
    private String url;
    private String name;

    public CallableDemo(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 {
        CallableDemo callableDemo1 = new CallableDemo("https://img2.baidu.com/it/u=450745774,808114680&fm=253&fmt=auto&app=138&f=JPEG?w=370&h=500","xixi01.jpg");
        CallableDemo callableDemo2 = new CallableDemo("https://img0.baidu.com/it/u=1145082949,2832027117&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=914","xixi02.jpg");
        CallableDemo callableDemo3 = new CallableDemo("http://t14.baidu.com/it/u=2571612918,2603191067&fm=224&app=112&f=JPEG?w=500&h=333","xixi03.jpg");
        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(1);
        //提交线程
        Future<Boolean> result1 = ser.submit(callableDemo1);
        Future<Boolean> result2 = ser.submit(callableDemo2);
        Future<Boolean> result3 = ser.submit(callableDemo3);
        //获取结果
        boolean r1 = result1.get();//此处的异常直接抛出
        boolean r2 = result2.get();
        boolean r3 = result3.get();
        //关闭服务
        ser.shutdownNow();
    }
}

 

运行结果:

4.初始并发问题

多个线程同时操作同一个对象,如购买过车票。

 

package com.java.multithreading;

public class
    //初识并发问题 。多个线程同时操作同一个对象,如购买过车票。线程紊乱,不安全。
ConcurrencyDemo implements Runnable{
    private int ticketNums = 5; //总票数
    @Override
    public void run() {
        while (true){
            if (ticketNums <= 0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"---->拿到了第"+ticketNums--+"票");

        }
    }

    public static void main(String[] args) {
        ConcurrencyDemo concurrencyDemo = new ConcurrencyDemo();
        //多条线程操作同一个实现类。即三条线程一起去抢总数为10的火车票。
        new Thread(concurrencyDemo,"一号").start();
        new Thread(concurrencyDemo,"二号").start();
        new Thread(concurrencyDemo,"三号").start();
    }

}

 

运行结果如下:

 

 

 会出现一号将票全部拿完的情况。

现在我们模拟延时看看:

package com.java.multithreading;

public class
    //初识并发问题 。多个线程同时操作同一个对象,如购买过车票。线程紊乱,不安全。
ConcurrencyDemo implements Runnable{
    private int ticketNums = 5; //总票数
    @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) {
        ConcurrencyDemo concurrencyDemo = new ConcurrencyDemo();
        //多条线程操作同一个实现类。即三条线程一起去抢总数为10的火车票。
        new Thread(concurrencyDemo,"一号").start();
        new Thread(concurrencyDemo,"二号").start();
        new Thread(concurrencyDemo,"三号").start();
    }

}

运行结果如下:

 

 

 出现了紊乱。此刻的线程就是不安全的。

5.静态代理模式

 

package com.java.multithreading;

//静态代理模式对比Thread
/*
  举例:结婚
  结婚需要实现结婚的接口。
  结婚不必新娘新郎亲自布置现场,可让婚庆公司帮忙
  结婚时只需新人到场举行婚礼即可。
  注意:真实对象和代理对象都要实现同一个接口
 */

public class StaticProxy {
    public static void main(String[] args) {
        Groom groom = new Groom();
        //WeddingCompany实现了Marry接口,Thread实现Runnable接口;
        new WeddingCompany(groom).Wedding();
        new Thread(() -> System.out.println("like")).start();
        //Thread类是一个代理,代理中间的真实对象,然后调用start方法
    }
}

//结婚的接口
interface Marry{
    void Wedding();//婚礼
}

//真实角色,实现结婚接口,目的是参加婚礼
class Groom implements Marry{
    @Override
    public void Wedding() {
        System.out.println("新郎参加婚礼");
    }
}

//代理角色,实现结婚接口,目的是筹备婚礼
class WeddingCompany implements Marry{
    private Marry target;

    public WeddingCompany(Marry target){
        this.target = target;
    }
    @Override
    public void Wedding() {
        System.out.println("婚庆公司筹备婚礼");
        this.target.Wedding();//然后真实对象来参加婚礼
    }
}

 

6.Lambda表达式

避免匿名内部类过多,其实质属于函数式编程的概念。

函数式接口的定义:

  • 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。

  如Runnable接口:

package java.lang;

@FunctionalInterface
public interface Runnable {
    void run();
}
  • 对于函数式接口,我们可以通过Lamda表达式来创建该接口对象。

推导 lambda表达式:

原始代码:

package com.java.multithreading;
//推导Lamda表达式
public class LambdaDemo {
    public static void main(String[] args) {
        IStudy iStudy = new IStudy();
        iStudy.lambda();
    }
}

//定义一个函数式接口
interface Study{
    void lambda();//在接口里的方法,就是抽象方法
}

//实现类
class IStudy implements Study{
    @Override
    public void lambda() {
        System.out.println("我在学习");
    }
}

 

静态内部类

package com.java.multithreading;
//推导Lamda表达式
public class LambdaDemo {
    //实现类
   static  class IStudy implements Study{
        @Override
        public void lambda() {
            System.out.println("我在学习");
        }
    }
    public static void main(String[] args) {
        IStudy iStudy = new IStudy();
        iStudy.lambda();
    }
}

//定义一个函数式接口
interface Study{
    void lambda();//在接口里的方法,就是抽象方法
}

局部内部类

package com.java.multithreading;
//推导Lamda表达式
public class LambdaDemo {

    public static void main(String[] args) {
        //实现类
         class IStudy implements Study{
            @Override
            public void lambda() {
                System.out.println("我在学习");
            }
        }
        IStudy iStudy = new IStudy();
         iStudy.lambda();
    }
}

//定义一个函数式接口
interface Study{
    void lambda();//在接口里的方法,就是抽象方法
}

匿名内部类

package com.java.multithreading;
//推导Lamda表达式
public class LambdaDemo {

    public static void main(String[] args) {
        //实现类
        Study iStudy = new Study() {
            @Override
            public void lambda() {
                System.out.println("我在学习");
            }
        };
       iStudy.lambda();
    }
}

//定义一个函数式接口
interface Study{
    void lambda();//在接口里的方法,就是抽象方法
}

用lambda简化

package com.java.multithreading;
//推导Lamda表达式
public class LambdaDemo {

    public static void main(String[] args) {
        //实现类
        Study iStudy = ()->{
                System.out.println("我在学习");
        };
       iStudy.lambda();
    }
}

//定义一个函数式接口
interface Study{
    void lambda();//在接口里的方法,就是抽象方法
}

 

 

说明:非原创,跟着b站狂神敲的

posted @ 2022-03-25 17:39  虞美人体重90  阅读(64)  评论(0编辑  收藏  举报