zoukankan      html  css  js  c++  java
  • java多线程笔记 b站狂神视频笔记

    线程创建

    Thread、Runnable、Callable

    继承Thread类实现Runnable接口 为重点,实现Callable接口仅作了解

    Thread

    • 自定义线程类继承Thread类
    • 重写run()方法,编写线程执行体
    • 创建线程对象,调用start()方法启动线程
    package com.company.dxc;
    
    // 创建线程的方式:继承Thread类 、重写run()方法、 调用start开启线程
    
    // 总结:线程开启不一定立即执行,由cpu进行调度执行
    
    public class TestThread01 extends Thread {
        @Override
        public void run() {
            // run方法线程体
            for (int i = 0; i < 200; i++) {
                System.out.println("正在执行线程----" + i);
            }
        }
    
        public static void main(String[] args) {
            // main主线程
            // 创建一个线程对象
            TestThread01 testThread01 = new TestThread01();
            //调用start()方法开启线程
            testThread01.start();
    
            for (int i = 0; i < 2000; i++) {
                System.out.println("正在执行主方法******" + i);
            }
        }
    }
    

    Runnable

    • 定义MyRunnable类实现Runnable接口
    • 实现run()方法,编写线程执行体
    • 创建线程对象,调用start()方法启动线程
    package com.company.dxc;
    // 创建线程方式2
    // 实现Runnable接口,重写run方法,执行线程需要丢入Runnable接口实现类 调用start执行
    public class TestThread02 implements Runnable {
        @Override
        public void run() {
            // run方法线程体
            for (int i = 0; i < 200; i++) {
                System.out.println("正在执行线程----" + i);
            }
        }
    
        public static void main(String[] args) {
            // main主线程
            // 创建Runnable接口的实现类对象
            TestThread01 testThread01 = new TestThread01();
            //  创建线程对象,通过线程对象来开启我们的线程,代理
            // Thread tr = new Thread(testThread01);
            // tr.start();
    
            new Thread(testThread01).start();
    
            for (int i = 0; i < 2000; i++) {
                System.out.println("正在执行主方法******" + i);
            }
        }
    }
    

    Thread与Runnable比较

    继承Thread类 实现Runnable接口
    子类继承Thread类具备多线程能力 实现接口Runnable具有多线程能力
    启动线程:子类对象.start(); 启动线程:传入目标对象+Thread对象.start();
    不建议使用:避免OOP单继承局限性 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

    例子1. 购买火车票

    通过本例子发现并发问题

    package com.company.dxc;
    // 多个线程同时操作同一个对象
    // 买火车票的例子
    
    // 问题:多个线程同时对一个资源进行操作 解决并发问题
    public class TestThread03 implements Runnable {
    
        // 票数
        private int ticketNums = 10;
    
        @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() + " --> 拿到了NO:" + ticketNums-- + "票" );
            }
        }
    
        public static void main(String[] args) {
            TestThread03 ts = new TestThread03();
            new Thread(ts, "stu").start();
            new Thread(ts, "teacher").start();
            new Thread(ts, "gangster").start();
        }
    }
    

    例子2. 龟兔赛跑

    package com.company.dxc;
    
    // 模拟龟兔赛跑
    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("rabbit") && i%10 == 0){
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
                // 判断比赛是否结束
                boolean flag = gameOver(i);
                // 如果比赛结束 停止程序
                if (flag) {break;}
    
                System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "steps");
            }
        }
    
        //判断是否完成比赛
        private boolean gameOver(int steps) {
            // 判断是否有胜利者
            if (winner != null) return true; // 已经存在胜利者
            {
                if (steps >= 100) {
                    winner = Thread.currentThread().getName();
                    System.out.println("winner is " + winner);
                    return true;
                }
            }
            return false;
        }
    
        public static void main(String[] args) {
            race r1 = new race();
            new Thread(r1, "rabbit").start();
            new Thread(r1, "tortoise").start();
    
        }
    }
    

    Callable(仅了解)

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

    静态代理

    个人结婚与婚庆公司的例子

    package com.company.dxc;
    
    // 静态代理模式:
    // 真实对象和代理对象都要实现同一个接口
    // 代理对象要代理真实角色
    
    // 好处 :
    // 1. 代理对象可以做很多真实对象做不了的事情
    // 2. 真实对象专注做自己的事情
    
    
    public class StaticProxy {
        public static void main(String[] args) {
            You you = new You(); // 真实对象
            new Thread(() -> System.out.println("i love you")).start(); // lambda表达式
            new WeddingCompany(new You()).HappyMarry();
    //        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("结婚之前,布置场所");
        }
    }
    

    Lambda表达式

    为什么要使用lambda表达式

    • 避免匿名内部类定义过多
    • 可以让代码看起来更简洁
    • 去掉无意义代码,留下核心逻辑

    注:只有一行代码的情况下才能简化成一行;前提是接口为函数式接口

    package com.company.dxc;
    /*
    * 推导lambda表达式
    */
    public class TestLambda {
    
        // 3. 静态内部类
        static class Like2 implements Ilike{
    
            @Override
            public void lambda() {
                System.out.println("i like lambda2!");
            }
        }
    
        public static void main(String[] args) {
            Ilike like = new Like();
            like.lambda();
    
            like = new Like2();
            like.lambda();
    
            // 4. 局部内部类
            class Like3 implements Ilike{
    
                @Override
                public void lambda() {
                    System.out.println("i like lambda3!");
                }
            }
            like = new Like3();
            like.lambda();
    
            // 5. 匿名内部类,没有类的名称,必须借助接口或者父类
            like = new Ilike() {
                @Override
                public void lambda() {
                    System.out.println("i like lambda4");
                }
            };
            like.lambda();
    
            // 6. 用lambda表达式
            like = () -> {
                System.out.println("i like lambda5");
            };
            like.lambda();
    
        }
    }
    
    // 1. 定义一个函数式接口
    interface Ilike{
        void lambda();
    }
    
    // 2. 实现类
    class Like implements Ilike{
        @Override
        public void lambda() {
            System.out.println("i like lambda!");
        }
    }
    

    线程状态

    创建状态、阻塞状态、死亡装填、就绪状态运行状态

    image-20210201162717555

    线程的方法

    image-20210201163727816

    停止线程

    • 不推荐使用JDK提供的stop(); destroy();方法
    • 推荐线程自己停止
    • 建议使用一个标志位进行终止变量
    package com.company.dxc;
    
    public class TestStop implements Runnable {
        // 1. 设置标志位
        private boolean flag = true;
    
        @Override
        public void run() {
            int i = 0;
            while(flag){
                System.out.println("Thread is running...." + i++);
            }
        }
    
        // 2. 设置一个公开的方法停止线程,转换标志位
    
        public void stop(){
            this.flag = false;
        }
    
        public static void main(String[] args) {
            TestStop testStop = new TestStop();
            new Thread(testStop).start();
            // main 先执行
            for (int i = 0; i < 1000; i++) {
                System.out.println("main is running..." + i);
                if(i == 900){
                    //调用stop方法停止线程
                    testStop.stop();
                    System.out.println("Thread stop!");
                }
            }
        }
    
    }
    

    线程休眠

    • sleep(时间)指定当前线程阻塞的毫秒数
    • sleep存在异常InterruptedException
    • sleep时间到达后线程进入就绪状态
    • sleep可以模拟网络延时,倒计时等
    • 每一个对象都有一个锁,sleep不会释放锁
    package com.company.dxc;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class TestSleep {
        public static void main(String[] args) throws InterruptedException {
          //  down();
            // 打印当前系统时间
            Date date = new Date(System.currentTimeMillis());
            while (true){
                try {
                    Thread.sleep(1000);
                    System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
                    date = new Date(System.currentTimeMillis()); // 更新当前时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void down() throws InterruptedException {
            int num = 10;
            while (true){
                Thread.sleep(1000);
                System.out.println(num --);
                if(num <= 0) break;
            }
        }
    }
    

    线程礼让

    • 礼让线程,让当前正在执行的线程暂停,但不阻塞
    • 将线程从运行状态转为就绪状态
    • 让CPU重新调度,礼让不一定成功,看CPU心情
    package com.company.dxc;
    // 测试不一定成功的线程礼让
    
    public class TestYield {
        public static void main(String[] args) {
            myYield myYield = new myYield();
            new Thread(myYield, "a").start();
            new Thread(myYield, "b").start();
        }
    }
    
    class myYield implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"线程开始执行");
            Thread.yield();
            System.out.println(Thread.currentThread().getName()+"线程停止执行");
        }
    }
    

    Join

    • join合并线程,待此线程执行完成后再执行其他线程,其他线程阻塞
    • 可以想象成插队
    package com.company.dxc;
    
    public class TestJoin implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 500; i++) {
                System.out.println("★ VIP Thread ☆" + i);
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            TestJoin testJoin = new TestJoin();
            Thread thread = new Thread(testJoin);
            thread.start();
    
            // 主线程
            for (int i = 0; i < 500; i++) {
                if(i == 200){
                    thread.join(); // 插队
                }
                System.out.println("main" + i);
            }
        }
    }
    

    线程优先级

    • java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,调度器按照优先级决定应该调度哪个线程来执行
    • 线程的优先级用数字表示,范围从1~10 Thread.MIN_PRIORITY = 1;
    • 使用以下方式改变或获取优先级 getPriority().setPriority(int xxx);

    优先级低只是意味着获得调度的概率低,并不是高优先级必然先调用 (性能倒置问题)

    package com.company.dxc;
    
    public class TestPriority extends Thread {
        public static void main(String[] args) {
            // 主线程优先级
            System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
    
            myPriority myPriority = new myPriority();
    
            Thread t1 = new Thread(myPriority);
            Thread t2 = new Thread(myPriority);
            Thread t3 = new Thread(myPriority);
            Thread t4 = new Thread(myPriority);
            Thread t5 = new Thread(myPriority);
    
            // 先设置优先级 再启动
            t1.start();
    
            t2.setPriority(1);
            t2.start();
    
            t3.setPriority(4);
            t3.start();
    
            t4.setPriority(Thread.MAX_PRIORITY); // Max 为最大 10
            t4.start();
    
            t5.setPriority(Thread.MIN_PRIORITY); // min 为最小 1
            t5.start();
    
        }
    }
    
    class myPriority implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
        }
    }
    

    守护线程(daemon)

    • 线程分为用户线程和守护线程
    • 虚拟机必须确保用户线程执行完毕
    • 虚拟机不用等待守护线程执行完毕
    • 如:后台记录操作日志、监控内存、垃圾回收 etc.
    package com.company.dxc;
    // 测试守护线程 上帝守护人类为例
    
    public class TestDaemon {
        public static void main(String[] args) {
            God god = new God();
            Human human = new Human();
            Thread thread = new Thread(god);
            thread.setDaemon(true); // 默认false表示用户线程,正常线程都是用户线程
    
            thread.start();// 守护线程启动
            new Thread(human).start(); // 用户线程启动
    
        }
    }
    
    // 上帝
    class God implements Runnable{
    
        @Override
        public void run() {
            while(true){ // 按理来说不会结束 但作为守护线程在用户线程结束后 随之结束(可能会伴随虚拟机关闭的一点点延迟)
                System.out.println("正在守护!");
            }
        }
    }
    
    // 人类
    class Human implements Runnable{
    
        @Override
        public void run() {
            for (int i = 0; i < 36500; i++) {
                System.out.println("每天都要开心活着!");
            }
            System.out.println("goodbye world!");
        }
    }
    

    线程同步

    并发:同一个对象被多个线程同时操作

    处理多线程问题时,多线程访问一个对象,并且某些线程还想修改这个对象,这时候就需要线程同步。线程同步是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕再让下一个线程使用

    队列与锁

    两个不安全线程程序的例子

    1. 不安全购票

    package com.company.dxc;
    
    import javax.swing.plaf.TableHeaderUI;
    
    //不安全买票
    // 线程不安全,有负数
    public class UnsafeBuyTicket {
        public static void main(String[] args) {
            BuyTicket station = new BuyTicket();
    
            new Thread(station, "买票人老壹 ").start();
            new Thread(station, "买票人老贰 ").start();
            new Thread(station, "黄牛 ").start();
        }
    }
    
    class BuyTicket implements Runnable{
        // 票
        private int ticketNum = 10;
        boolean flag = true; // 外部停止方式
        @Override
        public void run() {
            // 买票
            while(true){
                try {
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private void buy() throws InterruptedException {
            // 判断是否有票
            if(ticketNum <= 0){
                flag = false;
                return;
            }
    
            //模拟延时
            Thread.sleep(100);
    
            // 买票
            System.out.println(Thread.currentThread().getName() + "买到了" + ticketNum--);
        }
    }
    

    2. 不安全取款

    package com.company.dxc;
    
    // 不安全取钱
    // 两个人去取钱
    public class UnsafeBank {
        public static void main(String[] args) {
            // 账户
            Account account = new Account(100, "存款金额");
            Drawing you = new Drawing(account, 50, "你");
            Drawing gf = new Drawing(account, 100, "对方");
            you.start();
            gf.start();
        }
    }
    
    // 账户
    class Account{
        int money; // 余额
        String name; // 卡名
    
        public Account(int money, String name) {
            this.money = money;
            this.name = name;
        }
    }
    // 银行 模拟取款
    class Drawing extends Thread{
        Account account; // 账户
        int drawingMoney; // 取了多少钱
        int nowMoney; // 还剩多少钱
    
        public Drawing(Account account, int drawingMoney, String name){
            super(name);
            this.account = account;
            this.drawingMoney = drawingMoney;
        }
    
        //取钱操作
        @Override
        public void run() {
            // 判断有没有钱
            if(account.money - drawingMoney < 0){
                System.out.println(Thread.currentThread().getName() + "钱不够,取不了咯!");
                return;
            }
    
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            // 卡内余额 = 余额 - 你取的钱
            account.money = account.money - drawingMoney;
            // 你手里的钱
            nowMoney = nowMoney + drawingMoney;
    
            System.out.println(account.name + "余额为:" + account.money);
            // 此时 Thread.currentThread().getName() = this.getName()
            System.out.println(this.getName() + "手里的钱:" + nowMoney);
        }
    }
    

    同步方法

    • 由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized

      同步方法 public synchronized void method(int args){ }

    • synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行

    package com.company.dxc;
    
    import javax.swing.plaf.TableHeaderUI;
    
    //不安全买票
    // 线程不安全,有负数
    public class UnsafeBuyTicket {
        public static void main(String[] args) {
            BuyTicket station = new BuyTicket();
    
            new Thread(station, "买票人老壹 ").start();
            new Thread(station, "买票人老贰 ").start();
            new Thread(station, "黄牛 ").start();
        }
    }
    
    class BuyTicket implements Runnable{
        // 票
        private int ticketNum = 10;
        boolean flag = true; // 外部停止方式
        @Override
        public void run() {
            // 买票
            while(true){
                try {
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    		// *********此处进行同步方法************
        // synchronized 同步方法,锁的是this
        private synchronized void buy() throws InterruptedException {
            // 判断是否有票
            if(ticketNum <= 0){
                flag = false;
                return;
            }
    
            //模拟延时
            Thread.sleep(100);
    
            // 买票
            System.out.println(Thread.currentThread().getName() + "买到了" + ticketNum--);
        }
    }
    
    package com.company.dxc;
    
    // 不安全取钱
    // 两个人去取钱
    public class UnsafeBank {
        public static void main(String[] args) {
            // 账户
            Account account = new Account(100, "存款金额");
            Drawing you = new Drawing(account, 50, "你");
            Drawing gf = new Drawing(account, 100, "对方");
            you.start();
            gf.start();
        }
    }
    
    // 账户
    class Account{
        int money; // 余额
        String name; // 卡名
    
        public Account(int money, String name) {
            this.money = money;
            this.name = name;
        }
    }
    // 银行 模拟取款
    class Drawing extends Thread{
        Account account; // 账户
        int drawingMoney; // 取了多少钱
        int nowMoney; // 还剩多少钱
    
        public Drawing(Account account, int drawingMoney, String name){
            super(name);
            this.account = account;
            this.drawingMoney = drawingMoney;
        }
    
        //取钱操作
        @Override
        public void run() {
          	// ----------此时进行同步操作--------------
            // synchronized 默认锁的是this,所以此时用同步块对account进行同步
            synchronized (account){
                // 判断有没有钱
                if(account.money - drawingMoney < 0){
                    System.out.println(Thread.currentThread().getName() + "钱不够,取不了咯!");
                    return;
                }
    
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                // 卡内余额 = 余额 - 你取的钱
                account.money = account.money - drawingMoney;
                // 你手里的钱
                nowMoney = nowMoney + drawingMoney;
    
                System.out.println(account.name + "余额为:" + account.money);
                // 此时 Thread.currentThread().getName() = this.getName()
                System.out.println(this.getName() + "手里的钱:" + nowMoney);
            }
        }
    }
    

    死锁

    产生死锁的四个必要条件

    1. 互斥:一个资源每次只能被一个进程使用
    2. 请求与保持:一个进程因请求资源而阻塞时,对已获得的资源保持不放
    3. 不剥夺:进程已获得的资源,在未用完之前,不能强行剥夺
    4. 循环等待:若干进程之间形成一种头尾相接的循环等待资源关系
    package com.company.dxc;
    // 死锁:多个线程互相拥有对方需要的资源,形成僵持
    public class DeadLock {
        public static void main(String[] args) {
            Makeup moore = new Makeup(0, "Moore");
            Makeup dove = new Makeup(0, "Dove");
            moore.start();
            dove.start();
        }
    }
    
    class Lipstick{
    }
    class Mirror{
    }
    
    class Makeup extends Thread{
    
        // 需要的资源只有一份,用static来保证只有一份
        static Lipstick lipstick = new Lipstick();
        static Mirror mirror = new Mirror();
    
        int choice; // 选择
        String girlName; // 使用化妆品的人
    
        Makeup(int choice, String girlName){
            this.choice = choice;
            this.girlName = girlName;
        }
        @Override
        public void run() {
            // 化妆
            try {
                Makeup();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 化妆 互相持有对方的锁,就是需要拿到对方的资源
        private void Makeup() throws InterruptedException {
            if(choice == 0){
                synchronized (lipstick){
                    // 获得口红的锁
                    System.out.println(this.girlName + "获得口红的锁");
                    Thread.sleep(1000);
                }
                synchronized (mirror){
                    // 获得镜子的锁
                    System.out.println(this.girlName + "获得镜子的锁");
                }
            }else{
                synchronized (mirror){
                    // 获得口红的锁
                    System.out.println(this.girlName + "获得镜子的锁");
                    Thread.sleep(2000);
                }
                synchronized (lipstick){
                    // 获得镜子的锁
                    System.out.println(this.girlName + "获得口红的锁");
                }
            }
        }
    }
    

    Lock 锁

    class A{
      private final ReentrantLock lock = new ReenTrantLock();
      public void m(){
        lock.lock();
        try{
          // 保证线程安全的代码
        }
        finally{
    			lock.unlock();
          // 如果同步代码有异常,要将unlock()写入finally语句块 
        }
      }
    }
    

    synchronized 与 lock 的对比

    • Lock是显示锁,需要手动开启和关闭,synchronized为隐式锁,出了作用域自动释放
    • lock只有代码块锁,synchronized有代码块锁和方法锁
    • 使用lock锁,jvm将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性
    • 优先使用顺序
      • Lock > 同步代码快(已经进入方法体,分配了相应资源)> 同步方法(在方法体之外)

    线程协作 - 生产者消费者问题

    线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件

    • 对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,有需要马上通知消费者消费
    • 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费
    • 在生产者消费者问题中,仅有synchronized是不够的
      • synchronized可组织并发更新同一个共享资源,实现了同步
      • synchronized不能用来实现不同线程之间的消息传递(通信)

    Java提供了几个方法解决线程之间的通信问题

    方法名 作用
    wait() 表示线程一直邓艾,直到其他线程通知,与sleep()不同,会释放锁
    wait(long timeout) 指定等待的毫秒数
    notify() 唤醒一个处于等待状态的线程
    notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

    注意: 均是Object类的方法,都只能在同步方法或者同步代码快中使用,否则会抛出异常 IllegalMonitorStateException

    管程法

    • 生产者:负责生产数据的模块(可能是方法、对象、线程、进程)
    • 消费者:负责处理数据的模块(可能是方法、对象、线程、进程)
    • 缓冲区:消费者不能直接使用生产者的数据,利用中间“缓冲区”
    package com.company.dxc;
    
    // 测试 生产者消费者模型 --> 利用缓冲区解决:管程法
    
    public class TestPC {
        public static void main(String[] args) {
            SynBuffer synBuffer = new SynBuffer();
    
            new Producer(synBuffer).start();
            new Consumer(synBuffer).start();
        }
    }
    
    // 生产者
    class Producer extends Thread{
        SynBuffer buffer;
        public Producer(SynBuffer buffer){
            this.buffer = buffer;
        }
        // 生产
    
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("生产了" + i +"只鸡");
                try {
                    buffer.push(new Chicken(i));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    // 消费者
    class Consumer extends Thread{
        SynBuffer buffer;
        public Consumer(SynBuffer buffer){
            this.buffer = buffer;
        }
    
        // 消费
    
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    System.out.println("消费了-->" + buffer.pop().id +"只鸡");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    // 产品
    class Chicken{
        int id; // 产品编号
    
        public Chicken(int id) {
            this.id = id;
        }
    }
    
    // 缓冲区
    class SynBuffer{
    
        //容器大小
        Chicken[] chickens = new Chicken[10];
        // 容器计数器
        int count = 0;
    
        // 生产者放入产品
        public synchronized void push(Chicken chicken) throws InterruptedException {
            // 如果容器满了,需要等待消费者消费
            if(count == chickens.length){
                // 通知消费者消费,生产等待
                this.wait();
            }
            // 如果没有满,需要丢入产品
            chickens[count] = chicken;
            count ++;
            // 可以通知消费者消费了
            this.notifyAll();
        }
    
        // 消费者消费产品
        public synchronized Chicken pop() throws InterruptedException {
            // 判断能否消费
            if(count == 0){
                /// 等待生产者生产,消费者等待
                this.wait();
            }
            // 如果可以消费
            count --;
            Chicken chicken = chickens[count];
            // 吃完了,通知生产者生产
            this.notifyAll();
    
            return chicken;
        }
    }
    

    信号灯法

    package com.company.dxc;
    
    public class TestPC2 {
        public static void main(String[] args) {
            TV tv = new TV();
            new Player(tv).start();
            new Watcher(tv).start();
        }
    }
    
    // 生产者 --> 演员
    class Player extends Thread{
        TV tv;
        public Player(TV tv){
            this.tv = tv;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                if (i % 2 == 0){
                    this.tv.play("节目一:新闻联播");
                }else{
                    this.tv.play("节目二:法治在线");
                }
            }
        }
    }
    // 消费者 --> 观众
    class Watcher extends Thread{
        TV tv;
        public Watcher(TV tv){
            this.tv = tv;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                tv.watch();
            }
        }
    }
    
    // 产品 --> 节目
    class TV{
        // 演员表演,观众等待 T
        // 观众观看,演员等待 F
        String voice; // 表演的节目
        boolean flag = true;
        // 表演
        public synchronized void play(String voice){
    
            if (!flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            System.out.println("演员表演了:" + voice);
            // 通知观众观看
            this.notifyAll(); // 通知唤醒
            this.voice = voice;
            this.flag = !this.flag;
        }
        // 观看
        public synchronized void watch(){
            if (flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("观看了:" + voice);
            // 通知演员表演
            this.notifyAll();
            this.flag = !this.flag;
        }
    }
    

    线程池

    image-20210204153459575

    使用线程池

    image-20210204153549425

    package com.company.dxc;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class TestPool {
        public static void main(String[] args) {
            // 1. 创建服务,创建线程池
            // newFixedThreadPool 参数为线程池大小
            ExecutorService service = Executors.newFixedThreadPool(10);
            // 执行
            service.execute(new MyThread());
            service.execute(new MyThread());
            service.execute(new MyThread());
            service.execute(new MyThread());
            service.execute(new MyThread());
            // 2. 关闭连接
            service.shutdown();
        }
    }
    
    class   MyThread implements Runnable{
    
        @Override
        public void run() {
                System.out.println(Thread.currentThread().getName());
        }
    }
    

    总结

    package com.company.dxc;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class summary {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            new MyThread1().start();
    
            new Thread(new MyThread2()).start();
    
            FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
            new Thread(futureTask).start();
            Integer integer = futureTask.get();
            System.out.println(integer);
        }
    }
    
    // 1. 继承Thread类
    class MyThread1 extends Thread{
        @Override
        public void run() {
            System.out.println("My Thread1");
        }
    }
    // 2. 实现Runnable接口
    class MyThread2 implements Runnable{
        @Override
        public void run() {
            System.out.println("My Thread2");
        }
    }
    // 3. 实现Callable接口
    class MyThread3 implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            System.out.println("My Thread3");
            return 100;
        }
    }
    
  • 相关阅读:
    [笔记] 什么是欠采样?
    [笔记] Frequncy Divider
    [笔记] SDRAM读写控制
    能力去激活
    一个实用的SQL
    有用的SQL 语句(不断更新)
    javascript 与服务器端交互的一个小问题
    Javascript 验证码
    几个文本编辑器
    ASP.NET验证码(3种)
  • 原文地址:https://www.cnblogs.com/coderzjz/p/14347646.html
Copyright © 2011-2022 走看看