多线程 > 基础篇(一)

线程

初识线程

执行顺序

程序.进程.线程

  • 程序指令和数据的有序集合 ,是一个静态的概念。
  • 进程执行程序的依次执行过程 ,是一个动态的概念。是系统资源分配的单位。
  • 一个进程中可以包含若干个 线程 ,一个进程中至少有一个线程。线程是CPU调度和执行的单位。

线程创建

创建线程四种方法

  1. 继承Thread类,重写run()方法
  2. 实现Runnable()接口,重写run()方法
  3. 实现Callabl()接口,重写call()方法
  4. 线程池

1. 继承Thread类

不推荐使用,避免OOP单继承的局限性

自定义线程类继承 ‘Thread’ 类
重写 ‘run()’ 方法,编写线程执行体
创建线程对象,调用 ‘start()’ 方法启动线程
线程不一定立即执行,CPU安排调度

/**
 * 线程实现方式一:
 * 1. 继承Thread类
 * 2. 重写run方法
 * 3. 调用start开启线程
 *
 * 不推荐使用,避免OOP单继承的局限性
 */
public class ThreadExtends extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("我喝了" + (i + 1) + "口水!");
        }
    }

    public static void main(String[] args) {
        ThreadExtends threadExtends = new ThreadExtends();
        
     	// 只有 主线程main 一条执行路径  
        // run = 插队
        // threadExtends.run();

        // 多条执行路径
        // start = 开辟新的执行路径
        // 主线程main和子线程run交替执行
        threadExtends.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("第吃了" + (i + 1) + "口饭!");
        }
    }
}
/**
 * 继承Thread类实现多线程下载图片
 */
public class ThreadExtendsDemo extends Thread {

    public String url;
    public String name;

    public ThreadExtendsDemo(String url, String name) {
        this.url = url;
        this.name = name;
    }

    /**
     * 下载图片线程的执行体
     */
    @Override
    public void run() {
        // 重写run方法
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downLoad(url, name);
        System.out.println("下载了文件名为:" + name);
    }

    public static void main(String[] args) {
        ThreadExtendsDemo t1 = new ThreadExtendsDemo("http://cms-bucket.ws.126.net/2019/12/19/cf143f1465dc403c86cb7dc5e86a2c90.png", "WY_logo.jpg");
        ThreadExtendsDemo t2 = new ThreadExtendsDemo("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png", "baidu_log.jpg");
        ThreadExtendsDemo t3 = new ThreadExtendsDemo("https://docs.alibabagroup.com/assets2/images/cn/global/logo_header.png", "alibaba-log.jpg");

        /**
         * 线程不一定立即执行,CPU安排调度
         */
        t1.start();
        t2.start();
        t3.start();
    }

}

// 下载器
class WebDownloader {
    // 下载方法
    public void downLoad(String url, String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downLoad方法下载失败!");
        }
    }
}

2. 实现Runnable()接口

推荐使用Runnable对象,因为Java单继承的局限性

自定义线程类实现 Runnable 接口
实现 run() 方法,编写线程执行体
创建线程对象
丢入 Runnable 接口实现类,调用 start 开启线程(静态代理)

/**
 * 线程实现方式二:
 * 1. 实现Runnable接口
 * 2. 重写run方法
 * 3. 执行线程需要丢入Runnable接口实现类,调用start开启线程
 *
 * 推荐使用,避免了单继承的局限性,方便了同一个对象被多个线程使用
 */
public class ThreadRunnable implements Runnable {
    
  	@Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("我喝了" + (i + 1) + "口水!");
        }
    }

    public static void main(String[] args) {
        // 创建Runnable接口的实现类对象
        ThreadRunnable threadRunnable = new ThreadRunnable();
        // 创建线程对象,通过线程对象开启我们的线程。
        // 静态代理
        new Thread(threadRunnable).start();

        for (int i = 0; i < 100; i++) {
            System.out.println("第吃了" + (i + 1) + "口饭!");
        }
    }
}
/**
 * 多个线程操作同一个对象
 * 买火车票
 */
public class ThreadRunnableTicketDemo implements Runnable {

    // 票数
    private int ticketNum = 10;

    @Override
    public void run() {
        while (true) {
            if (ticketNum <= 0) {
                break;
            }

            // 模拟延时:提高问题的发生性
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + " --> 拿到了第" + ticketNum-- + "张票");
        }
    }

    public static void main(String[] args) {
        ThreadRunnableTicketDemo ticketThread = new ThreadRunnableTicketDemo();
        new Thread(ticketThread, "张三").start();
        new Thread(ticketThread, "李四").start();
        new Thread(ticketThread, "黄牛党").start();
        // 发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱。
    }
}

3. 实现Callable()接口

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

好处:

可以定义返回值
可以抛出异常

/**
 * 线程实现方式三:
 * 1. 实现Callable接口
 * 2. 重写call方法
 * 3. 创建目标对象
 * 4. 创建执行服务 ExecutorService service = Executors.newFixedThreadPool(3);
 * 5. 提交执行 Future<Boolean> submit1 = service.submit(t1);
 * 6. 获取执行结果 Boolean aBoolean1 = submit1.get();
 * 7. 关闭服务 service.shutdownNow();
 * <p>
 * 好处:
 * 1. 可以定义返回值
 * 2. 可以抛出异常
 */
public class ThreadCallable implements Callable<Boolean> {

    public String url;
    public String name;

    public ThreadCallable(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() throws Exception {
        // 重写run方法
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downLoad(url, name);
        System.out.println("下载了文件名为:" + name);
        return true;
    }

    public static void main(String[] args) {
        ThreadCallable t1 = new ThreadCallable("http://cms-bucket.ws.126.net/2019/12/19/cf143f1465dc403c86cb7dc5e86a2c90.png", "WY_logo.jpg");
        ThreadCallable t2 = new ThreadCallable("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png", "baidu_log.jpg");
        ThreadCallable t3 = new ThreadCallable("https://docs.alibabagroup.com/assets2/images/cn/global/logo_header.png", "alibaba-log.jpg");

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

        // 提交执行
        Future<Boolean> submit1 = service.submit(t1);
        Future<Boolean> submit2 = service.submit(t2);
        Future<Boolean> submit3 = service.submit(t3);

        try {
            // 获取执行结果
            Boolean aBoolean1 = submit1.get();
            Boolean aBoolean2 = submit2.get();
            Boolean aBoolean3 = submit3.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            // 关闭服务
            service.shutdownNow();
        }
    }
}

// 下载器
class WebDownloader {
    // 下载方法
    public void downLoad(String url, String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downLoad方法下载失败!");
        }
    }
}

4. 线程池(JUC)

线程池提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提交了响应速度。

  • 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。
    可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
  • 好处:
    * 提高响应速度(减少了创建新线程的时间)
    * 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
    * 便于线程管理…
    • corePoolSize:核心池的大小
    • maximumPoolSize:最大线程数
    • keepAliveTime::线程没有任务时最多保持多长时间后会终止
  1. JDK 5.0起提供了线程池相关API: ExecutorService 和Executors
  2. ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
  3. void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执Runnable
  4. Future submit(Callable task):执行任务,有返回值,一般用来执行Callable
  5. void shutdown() :关闭连接池
  6. Executors: 工具类、线程池的工厂类,用于创建并返回不同类型的线程池
/**
 * 测试线程池
 * service.execute(threadPool);
 * 执行任务/命令,没有返回值,一般用来执行Runnable
 */
public class ThreadPool {

    public static void main(String[] args) {

        // 1. 创建线程池服务
        // newFixedThreadPool(线程池大小)
        ExecutorService service = Executors.newFixedThreadPool(10);

        ThreadRunnable threadRunnable = new ThreadRunnable();

        // 2. 执行
        service.execute(threadRunnable);
        service.execute(threadRunnable);
        service.execute(threadRunnable);
        service.execute(threadRunnable);
        service.execute(threadRunnable);

        //3. 关闭服务
        service.shutdown();
    }
}

class ThreadRunnable implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}
/**
 * 测试线程池
 * service.submit(threadCallable);
 * 执行任务,有返回值,一般用来执行Callable
 */
public class ThreadPool2 {

    public static void main(String[] args) {

        // 1. 创建线程池服务
        // newFixedThreadPool(线程池大小)
        ExecutorService service = Executors.newFixedThreadPool(10);

        ThreadCallable threadCallable = new ThreadCallable();

        // 2. 提交执行
        Future<Boolean> submit1 = service.submit(threadCallable);
        Future<Boolean> submit2 = service.submit(threadCallable);
        Future<Boolean> submit3 = service.submit(threadCallable);
        Future<Boolean> submit4 = service.submit(threadCallable);
        Future<Boolean> submit5 = service.submit(threadCallable);

        //3. 关闭服务
        service.shutdown();
    }

}

class ThreadCallable implements Callable<Boolean> {

    @Override
    public Boolean call() {
        System.out.println(Thread.currentThread().getName());
        return true;
    }
}

静态代理

举例:婚庆公司代理结婚对象,处理结婚事宜。
在这里插入图片描述

/**
 * 静态代理
 *
 * 真实对象和代理对象都要实现同一个接口
 * 代理对象必须要代理真实对象
 *
 * 好处:
 * 代理对象可以做很多真实对象做不了的事情
 * 真实对象专注做自己的事情
 */
public class StaticProxy {

    public static void main(String[] args) {
        // 你要结婚
        You you = new You();
        // 婚庆公司帮助你结婚
        /*WeddingCompany weddingCompany = new WeddingCompany(you);
        weddingCompany.happyMarry();*/

        // 对比: Thread就是个代理对象
        new Thread(() -> System.out.println("我爱你!")).start();
        new WeddingCompany(you).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 after() {
        System.out.println("结婚之后,收尾款!");
    }

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

总结
真实对象和代理对象都要实现一个接口
代理对象要代理真实角色
好处
代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事

汇总:线程结合lamda使用其实也是静态代理

new Thread(() -> System.out.println("我爱你!")).start();

Lamda

Lamda来由

先看Lamda解决了什么问题:

  • 避免匿名内部类定义过多
  • 其实质属于函数式编程的概念
  • 去掉了一堆没有意义的代码,只留下核心逻辑

学习Lamde首先要提到 函数式接口(Functional Interface) ,这是学习Java8 lamda表达式的关键

函数式接口

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

# 例如Runnable接口,就是一个函数式接口
public interface Runnable{
    public abstract void run();
}

Lamda表达式推导

public class LambdaWithNotParameter {

    // 3. 静态内部类
    static class StaticInnerClasses implements ILike {
        @Override
        public void lambda() {
            System.out.println("我是静态内部类");
        }
    }

    public static void main(String[] args) {
        ILike like = new Like();
        like.lambda();
        like = new StaticInnerClasses();
        like.lambda();
        // 4. 局部内部类
        class LocalInnerClasses implements ILike {
            @Override
            public void lambda() {
                System.out.println("我是局部内部类");
            }
        }
        like = new LocalInnerClasses();
        like.lambda();
        // 5. 匿名内部类,没有类的名称,必须借助接口或者父类
        like = new ILike() {
            @Override
            public void lambda() {
                System.out.println("我是匿名内部类");
            }
        };
        like.lambda();
        // 6. Lambda简化
        like = () -> {
            System.out.println("我是Lambda");
        };
        like.lambda();
    }
}

/**
 * 1. 定义函数式接口
 * 接口中只有一个方法的接口
 */
interface ILike {
    void lambda();
}

/**
 * 2. 实现类
 */
class Like implements ILike {
    @Override
    public void lambda() {
        System.out.println("我是内部类");
    }
}

总结

学习完以上内容,我学会了:

  1. 创建线程的四种方式,分别是继承Thread类,实现Runnable接口,实现Callable,线程池创建。
  2. 体会到线程的静态代理模式。
  3. 懂了Lambda表达式。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值