多线程-1

多线程-1

1.进程和线程

进程:进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。进程是一种抽象的概念,从来没有统一的标准定义。进程一般由程序,数据集合和进程控制块三部分组成。

线程:线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。

main线程、垃圾回收线程、守护线程

进程和线程的区别

  1. 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;

  2. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线

  3. 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信号等),某进程内的线程在其他进程不可见;

  4. 调度和切换:线程上下文切换比进程上下文切换要快得多

2.线程创建方式

每个线程都有优先权,具有较高优先级的线程优先于优先级较低的线程。

两种创建线程的方式:

继承Thread类
  1. 自定义线程类继承Thread类
  2. 这个子类重写run方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
public class TestThread extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码---" + i);
        }
    }

    public static void main(String[] args) {
        //main线程 主线程
        //创建一个线程对象
        TestThread testThread1 = new TestThread();
        //调用start方法开启线程
        testThread1.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习多线程---" + i);
        }
    }
}

总结:

线程开启不会立即执行,由cpu调度执行,交替执行

下载图片案例
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() {
        //
        WebDownload webDownload = new WebDownload();
        webDownload.download(url,name);
        System.out.println("下载了文件名为"+name);
    }
}

下载器

public class WebDownload{
    //下载方法
    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异常,downloader方法出现问题");
        }
    }
}

测试

public class App {
    public static void main(String[] args) {
        TestThread2 t1 = new TestThread2("https://img2.baidu.com/it/u=1272199222,1881530807&fm=26&fmt=auto","2.jpg");
        TestThread2 t2 = new TestThread2("https://img0.baidu.com/it/u=3796573490,363191689&fm=26&fmt=auto","3.jpg");
        TestThread2 t3 = new TestThread2("https://img0.baidu.com/it/u=3578816651,4122208081&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=313","4.jpg");

        t1.start();
        t2.start();
        t3.start();
    }
}

执行结果

下载了文件名为2.jpg
下载了文件名为3.jpg
下载了文件名为4.jpg
实现Runnable接口(重点)
  • Thread构造方法

    方法名 说明
    Thread(Runnable target) 分配一个新的Thread对象
    Thread(Runnable target, String name) 分配一个新的Thread对象
  • 实现步骤

    • 定义一个类MyRunnable实现Runnable接口
    • 在MyRunnable类中重写run()方法
    • 创建MyRunnable类的对象
    • 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
    • 启动线程
  • 代码演示

    public class MyRunnable implements Runnable {
        @Override
        public void run() {
            for(int i=0; i<100; i++) {
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
    public class MyRunnableDemo {
        public static void main(String[] args) {
            //创建MyRunnable类的对象
            MyRunnable my = new MyRunnable();
    
            //创建Thread类的对象,把MyRunnable对象作为构造方法的参数
            //Thread(Runnable target)
    //        Thread t1 = new Thread(my);
    //        Thread t2 = new Thread(my);
            //Thread(Runnable target, String name)
            Thread t1 = new Thread(my,"坦克");
            Thread t2 = new Thread(my,"飞机");
    
            //启动线程
            t1.start();
            t2.start();
        }
    }
    
实现Callable接口
  • 方法介绍

    方法名 说明
    V call() 计算结果,如果无法计算结果,则抛出一个异常
    FutureTask(Callable callable) 创建一个 FutureTask,一旦运行就执行给定的 Callable
    V get() 如有必要,等待计算完成,然后获取其结果
  • 实现步骤

    • 定义一个类MyCallable实现Callable接口
    • 在MyCallable类中重写call()方法
    • 创建MyCallable类的对象
    • 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数
    • 创建Thread类的对象,把FutureTask对象作为构造方法的参数
    • 启动线程
    • 再调用get方法,就可以获取线程结束之后的结果。
  • 代码演示

    public class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            for (int i = 0; i < 100; i++) {
                System.out.println("跟女孩表白" + i);
            }
            //返回值就表示线程运行完毕之后的结果
            return "答应";
        }
    }
    public class Demo {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //线程开启之后需要执行里面的call方法
            MyCallable mc = new MyCallable();
    
            //Thread t1 = new Thread(mc);
    
            //可以获取线程执行完毕之后的结果.也可以作为参数传递给Thread对象
            FutureTask<String> ft = new FutureTask<>(mc);
    
            //创建线程对象
            Thread t1 = new Thread(ft);
    
            String s = ft.get();
            //开启线程
            t1.start();
    
            //String s = ft.get();
            System.out.println(s);
        }
    }
    
  1. 可以定义返回值
  2. 可以抛出异常
  • 三种实现方式的对比
    • 实现Runnable、Callable接口
      • 好处: 扩展性强,实现该接口的同时还可以继承其他的类
      • 缺点: 编程相对复杂,不能直接使用Thread类中的方法
    • 继承Thread类
      • 好处: 编程比较简单,可以直接使用Thread类中的方法
      • 缺点: 可以扩展性较差,不能再继承其他的类
初识并发
package com.cloudcore.threadtest;

/**
 * @Author: hepeng
 * @Date: 2021/12/14 21:52
 */
/**
 * 初识并发问题
 * 并发:多个线程同时操作同一个对象
 * 买火车票例子
 */
public class TestThread4 implements Runnable{
    //火车票数
    private int ticketNums = 10;
    @Override
    public void run() {
        while(true){
            if (ticketNums <= 0) {
                break;
            }
            try {
                Thread.sleep(200L);
            } 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张票
飞机拿到了第8张票
火车拿到了第7张票
汽车拿到了第6张票
飞机拿到了第5张票
火车拿到了第4张票
汽车拿到了第4张票
飞机拿到了第3张票
火车拿到了第1张票
汽车拿到了第2张票
飞机拿到了第0张票

发现问题:汽车和飞机都拿到了第4张票

多个线程操作同一个资源时,线程不安全了,数据紊乱

龟兔赛跑
package com.cloudcore.threadtest;

/**
 * @Author: hepeng
 * @Date: 2021/12/14 22:03
 */
public class Race implements Runnable{
    //胜利者
    private static String winner;

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            //模拟兔子休息
            if (Thread.currentThread().getName().equals("兔子") && i%10==0){
                //兔子睡觉
                try {
                    Thread.sleep(100L);
                } 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;
        }else {
            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();
    }
}

3.静态代理模式

你:真实角色

婚庆公司:代理你,帮你处理结婚的事

结婚:实现结婚接口

静态代理模式总结:

真实对象和代理对象都要实现同一个接口

代理对象要代理真实角色

package com.cloudcore.threadtest;

/**
 * @Author: hepeng
 * @Date: 2021/12/14 22:25
 */
public class StaticProxy {
    public static void main(String[] args) {
        //真实结婚对象
        You you = new You();
        //代理对象
        WeddingCompany weddingCompany = new WeddingCompany(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 after() {
        System.out.println("结婚之后收尾款");
    }

    private void before() {
        System.out.println("结婚前布置现场");
    }
}
new Thread(()->{
    System.out.println("我稀罕你!");
}).start();
//------------------类比----------------------
new WeddingCompany(new You()).HappyMarry();

4.Lambda表达式

为什么要用lambda表达式

  • 避免匿名内部类定义过多
  • 可以让代码更简洁
  • 去掉了一堆没有意义的代码,只留下核心逻辑
package com.cloudcore.threadtest;

/**
 * @Author: hepeng
 * @Date: 2021/12/14 22:48
 */
public class TestLambda {
    /**
     * 静态内部类
     * @param
     */
    static class StaticLike implements ILike {
        @Override
        public void lambda() {
            System.out.println("i like static lambda");
        }
    }

    public static void main(String[] args) {
        ILike like = new Like();
        like.lambda();

        //静态内部类
        like = new StaticLike();
        like.lambda();

        /**
         * 局部内部类
         */
        class JubuLike implements ILike {
            @Override
            public void lambda() {
                System.out.println("i like jubu lambda");
            }
        }

        //4.局部内部类
        like = new JubuLike();
        like.lambda();

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

        //6.lambda表达式简化
        like = ()->System.out.println("i like single lambda");
        like.lambda();

    }
}
/**
 * 1.定义一个函数式接口
 */
interface ILike{
    void lambda();
}

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

结果:

i like lambda
i like static lambda
i like jubu lambda
i like niming lambda
i like single lambda

总结:

  1. lambda表达式只能有一行代码的情况下才能简化为一行,如果有多行就用代码块包裹

  2. 使用前提是接口为函数式接口(接口中只有一个抽象方法的方法)

  3. 多个参数也可以去掉参数类型,要去掉都去掉

posted @ 2021-10-11 21:00  正在努力的澎澎  阅读(55)  评论(0)    收藏  举报