多线程02_线程创建

1 三种创建方式

  • 继承Thread
  • 实现Runnable接口
  • 实现Callable接口

2 继承Thread

public class MyThread extends Thread {
    // 线程入口点
    @Override
    public void run() {
        // 线程体
        for (int i = 0; i < 20; i++) {
            System.out.println(i);
        }
    }
    
    public static void main(String[] args) {
        // 创建线程对象
        MyThread myThread = new MyThread();
        // 启动线程
        myThread.start();
    }
}
  • 自定义线程类继承Thread
  • 重写run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
  • 调用start()方法后线程不一定立即执行,需要CPU安排调度
  • 不建议使用此方法,会由于单继承产生局限性

3 实现Runnable接口

public class MyThread implements Runnable {
    @Override
    public void run() {
        // 线程体
        for (int i = 0; i < 20; i++) {
            System.out.println(i);
        }
    }
    
    public static void main(String[] args) {
        // 创建实现类对象
        MyThread myThread = new MyThread();
        // 创建代理类对象
        Thread thread = new Thread(myThread);
        // 启动
        thread.start();
    }
}
  • 自定义线程类实现Runnable接口
  • 重写run()方法,编写线程执行体
  • 创建实现类对象,并将其作为Thread类的参数创建线程对象
  • 调用线程对象的start()方法启动线程
  • 推荐使用实现Runnable接口的方法创建线程,这样可以避免Java单继承的局限性,方便同一个对象被多个线程使用

4 初识并发

模拟网站抢火车票场景,此时多个线程同时操作同一个对象:

public class MyThread implements Runnable {
    // 共有10张票
    private int ticketNums = 10;

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

            try {
                // 延时100ms
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // Thread.currentThread()返回当前线程对象
            // getName()方法返回线程对象名称
            System.out.println(Thread.currentThread().getName() + "抢到了第" + ticketNums-- + "张票");
        }
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();

        // 三个用户一同抢票
        // Thread类可以在new时为线程赋予名称
        new Thread(myThread, "黄牛").start();
        new Thread(myThread, "小张").start();
        new Thread(myThread, "大黄").start();
    }
}

程序运行结果如下:

黄牛抢到了第10张票
大黄抢到了第9张票
小张抢到了第8张票
黄牛抢到了第7张票
小张抢到了第6张票
大黄抢到了第5张票
大黄抢到了第3张票
小张抢到了第2张票
黄牛抢到了第4张票
小张抢到了第1张票
大黄抢到了第1张票

此时发现问题,在多线程下操作同一个资源,会产生数据紊乱的线程不安全问题,例如上述代码中实际运行时有两个用户获取了同一张票

5 案例:龟兔赛跑

龟兔赛跑需求如下:

  • 首先需要赛道距离,然后需要离终点越来越近
  • 判断比赛是否结束
  • 打印出胜利者
  • 龟兔赛跑开始
  • 兔子在途中会休息,需要模拟兔子休息
  • 最终乌龟赢得比赛
public class Race implements Runnable {
    // 记录胜者
    private static String winner;

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

        // 跑完全程则比赛结束,并为winner赋予当前线程名称
        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();
    }
}

在使用Object类的equals()方法判断是否相等时,如果比较双方一方是变量,一方是常量,最好将常量放在equal()方法的左侧,防止变量值为null产生空指针异常

6 实现Callable接口

public class TestCallable implements Callable<String> {
    // 需要重写call()方法,该方法有返回值,且需要抛出异常
    @Override
    public String call() throws Exception {
        return Thread.currentThread().getName();
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1 = new TestCallable();
        TestCallable t2 = new TestCallable();
        TestCallable t3 = new TestCallable();

        // 创建执行服务
        // 由线程池创建,数量为3
        ExecutorService service = Executors.newFixedThreadPool(3);

        // 提交执行
        Future<String> future1 = service.submit(t1);
        Future<String> future2 = service.submit(t2);
        Future<String> future3 = service.submit(t3);

        // 获取结果
        String result1 = future1.get();
        String result2 = future2.get();
        String result3 = future3.get();

        System.out.println(result1);
        System.out.println(result2);
        System.out.println(result3);

        // 关闭服务
        service.shutdown();
    }
}
  • 实现Callable接口,需要返回值类型
  • 重写call()方法,需要抛出异常
  • 创建目标对象
  • 创建执行服务:ExecutorService service = Executors.newFixedThreadPool(3);
  • 提交执行:Future<String> future1 = service.submit(t1);
  • 获取结果:String result1 = future1.get();
  • 关闭服务:service.shutdown();

7 静态代理

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 {
    private String name;

    public You(String name) {
        this.name = name;
    }

    @Override
    public void happyMarry() {
        System.out.println(name + "马上要结婚了");
    }
}

// 代理角色
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("结婚之后,收拾会场");
    }
}
  • 代理模式的核心思想:代理人跟被代理人都能做同一件事,只不过被代理人把执行权限交给了代理人,外部只能通过代理人来联系被代理人,通过代理人来增强被代理人的一些功能。

  • 真实对象(被代理对象)和代理对象都要实现同一个接口

  • Thread类的使用中也使用到了静态代理

    public static void main(String[] args) {
        // Thread类也实现了Runnable接口
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                
            }
        });
    }
    

8 Lambda表达式

  • Lambda表达式用于简化程序,避免匿名内部类定义过多

  • 其本质属于函数式编程

    (params) -> expression[表达式]
    (params) -> statement[语句]
    (params) -> {statements}
    
  • 函数式接口:一个接口如果只包含唯一一个抽象方法,那么它就是一个函数式接口。对于函数式接口,可以通过lambda表达式来创建该接口的对象

    public interface Runnable {
        public abstract void run();
    }
    
  • 在Java8后,可以在接口前在@FunctionalInterface注解表示改接口为一个函数式接口,此时接口中只能有一个抽象方法

    @FunctionalInterface
    public interface MyInterface {
        void func1();
        // void func2();	不能有多余的抽象方法 
    }
    
  • lambda表达式用于简化匿名内部类

    public class TestLambda {
        // 静态内部类
        static class Like2 implements ILike {
            @Override
            public void lambda() {
                System.out.println("I like lambda2");
            }
        }
    
        public static void main(String[] args) {
            ILike like = new Like1();
            like.lambda();
    
            like = new Like2();
            like.lambda();
    
            // 局部内部类
            class Like3 implements ILike {
                @Override
                public void lambda() {
                    System.out.println("I like lambda3");
                }
            }
    
            like = new Like3();
            like.lambda();
    
            // 匿名内部类,没有类的名称,必须借助接口或父类
            like = new ILike() {
                @Override
                public void lambda() {
                    System.out.println("I like lambda4");
                }
            };
            like.lambda();
    
            // lambda表达式
            like = () -> System.out.println("I like lambda5");
            like.lambda();
    
        }
    }
    
    // 函数式接口
    @FunctionalInterface
    interface ILike {
        void lambda();
    }
    
    // 实现类
    class Like1 implements ILike{
        @Override
        public void lambda() {
            System.out.println("I like lambda1");
        }
    }
    
  • 函数式接口中的抽象方法中存在一个参数时

    public class TestLambda {
        public static void main(String[] args) {
            ILove love = null;
            
            love = (String name) -> {
                System.out.println("I love " + name);
            };
            
            // 简化1:可以省略参数类型
            love = (name) -> {
                System.out.println("I love " + name);
            };
            
            // 简化2:可以省略括号
            love = name -> {
                System.out.println("I love " + name);
            };
            
            // 简化3:可以省略大括号
            // 省略大括号只能在lambda表达式只有一行代码的情况下使用
            love = name -> System.out.println("I love " + name);
            
            love.love("kayne");
        }
    }
    
    // 函数式接口
    @FunctionalInterface
    interface ILove {
        void love(String name);
    }
    
  • 函数式接口中的抽象方法中存在多个参数时

    public class TestLambda {
        public static void main(String[] args) {
            ILove love = null;
            
            love = (String name1, String name2) -> {
                System.out.println("I love " + name1 + " and " + "name2");
            };
            
            // 多个参数时也可以去掉参数类型,但要去掉就全部去掉
            // 多参数时不能省略圆括号
            love = (name1, name2) -> System.out.println("I love " + name1 + " and " + "name2");
            
            love.love("kayne");
        }
    }
    
    // 函数式接口
    @FunctionalInterface
    interface ILove {
        void love(String name1, String name2);
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值