JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

这篇具有很好参考价值的文章主要介绍了JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  • 欢迎光临 ^ V ^

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

1. 定时器

  • 定时器,可以理解为闹钟
    • 我们设立一个时间,时间一到,让一个线程跑起来~
  • 而Java标准库提供了一个定时器类:
    • Timer ,from java.util

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

1.1 定时器Timer的使用

1.1.1 核心方法schedule

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 传入任务引用(TimerTask task)和 “定时”(long delay / ms)

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 由于TimerTask不是函数式接口,是普通的抽象类
    • 所以只能用匿名内部类,而不能用lambda表达式

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 写法
public static void main(String[] args) {
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("好耶 ^ v ^");
            }
        }
    },1000);
    System.out.println("不好耶 T . T");
}

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • TimerTask实现了Runnable

    • 不能传Runnable对象过去,这属于向下转型~
  • JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

    • 是Runnable的一个“封装”
    • 所以,重写run方法,合情合理~
    • 只不过不能用
  • 而在Timer的schedule方法内部,则将这个线程保存起来,定时后执行~

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 而这,有一个细节,就是执行完后,程序并没有结束,进程并没退出

原因是:

  • Timer内置了一个前台线程
    • 阻止进程退出~
    • 这并不是重点,其实就是timer在等待被安排下一个任务~
1.1.2 定时器管理多个线程
public class Test {

    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("星期一好耶 ^ v ^");
            }
        },1000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("星期二好耶 ^ v ^");
            }
        },2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("星期三好耶 ^ v ^");
            }
        },3000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("星期四好耶 ^ v ^");
            }
        },4000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("星期五好耶 ^ v ^");
            }
        },5000);
        System.out.println("今天不好耶 T . T");
    }

}
  • 那么就安排多个任务呗~

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

1.1.3 定时器的使用场景
  • 应用场景特别多

    • 尤其是网络编程
  • 而这个任务等待,不应该是无期限的

    • 超时:504 【gateway timeout】
  • 定时器可以强制终止请求:浏览器内部都有一个定时器,发送请求后,定时器就开始定时;若在规定时间内,响应数据没有返回,就会强制终止请求
    JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 这个方法一般在任务的run方法中调用,确定是否及时

    • 这种特殊语法不是我们能理解的,并且目前我们不需要用到这个用法~

1.2 自己实现一个定时器

  • 想法一,根据任务们的时间
    • 在添入的时候,就让他们启动并以对应的时间"睡下"
      • 有点像睡眠排序法这个消遣的笑话~
    • 显然这个方法是不科学的,线程到达一个量级,进程必然装不下
    • 系统必然卡死崩掉
  • 想法二,根据时间,到了时间自动启动~
    • 将任务们按照时间长短排序
    • 每次只看最早启动的任务就好
      • 当然,等待时间是同步的~
      • 每个任务都有在等
    • 启动,再去看接下来的任务~
      • 如果两个任务同时启动,顺序则不能确定~

是不是触动你的DNA了?

  • 没错,搞一个堆就好了
    • 每次可见堆顶元素~
    • 而小根堆堆顶正是我们这里的最早启动的任务~
    • 旧堆顶取走后,新堆顶又是剩余的最早启动的任务~
  • 而定时器的核心数据结构就是:优先级队列 ===> 堆
    • 而定时器可能被多线程使用,所以线程安全问题也要被保证
    • 队列为空,队列为“满”的时候,对操作也要有限制(不应该有无限个任务)
    • 这就需要我们的阻塞队列~

即,定时器底层就是一个阻塞优先级队列! ===> PriorityBlockingQueue

  • 对于PriorityBlockingQueue,我这里并不会去模拟~
1.2.1 属性
class MyTask {
    public Runnable runnable;
    public long time;
}
public class MyTimer {
    private PriorityBlockingQueue<MyTask> tasks = new PriorityBlockingQueue<>();
    
}

阻塞优先级队列中的元素应该有如下两个信息:

  • MyTask
  1. 执行什么任务~
  2. 任务什么时候执行~

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

1.2.2 建立一个MyTask对象
  1. runnable就是一个任务~
  2. time是绝对时间,而不是定时时间
    • 是”启动时间“的具体时间
    • 到达这个时间,任务才能运行~
    • 为1970.01.01那一天的00:00:00到构建对象时的此时此刻的毫秒数~
  • 获取当前时间方法:System.currentTimeMillis()
class MyTask {
    public Runnable runnable;
    public long time;
    //绝对时间戳~
    //方便判断~
    //这个不是定时时间

    public MyTask(Runnable runnable, long delay) {
        this.runnable = runnable;
        this.time = delay + System.currentTimeMillis();
    }
}
1.2.3 schedule方法
public void schedule(Runnable runnable, long delay) {
    MyTask myTask = new MyTask(runnable, delay);
    tasks.put(myTask);
}
  • 构造一个myTask对象插入到队列中~
1.2.4 构造方法初步设计
public MyTimer() {
        Thread t = new Thread(() -> {
            try {
                MyTask myTask = tasks.take();
                long nowTime = System.currentTimeMillis();
                if(myTask.time <= nowTime) {
                    //启动
                }else {
                    //不能启动
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
}
  • 定时器被构造出来后,应该就已经启动“母线程”
    • 就应该尝试【take】了
    • 只不过队列为空,要阻塞等待~
  • 之后通过schedule安排任务~【put】

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 启动:
    • 调用run方法
  • 不能启动:
    • 将任务返回队列
1.2.5 构造方法最终设计
  • 在构造方法初步设计有两个很严重的BUG
    • 可以停止观看去想一想~
  1. 优先级对于自定义类,需要我们给“比较规则”,“优先级规则”
  2. “没有等待”以及“盲目等待”

对于1. 比较规则:

  • 只需要让MyTask实现比较接口

  • 当然也可以传比较器~(lambda表达式)

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 两种方式都OK~

  • 左减右大于0

    • 如果代表此对象大于该对象代表升序排列 ===> 小根堆

    • 如果代表此对象小于该对象代表降序排列 ===> 大根堆

对于2. “没有等待”以及“盲目等待”

  • 上述代码只会判断一次~
    • 应该套上一个循环~

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • wait等待,唤醒起来比较方便安全

    • sleep不是一个很好的选择~
    • 因为新任务的插入,要进行唤醒
    • 超过限定时间,自动醒来
      • wait需要有锁,这里我把循环体整个框起来了
      • 我用的是“同步锁”
  • “盲目等待” 代表,这里放回去后,计算器又会判断是否可启动

    • 这样就会导致一段时间内,这个任务反复被拿来拿去无数次~
    • 相当于,上课时看表,一秒看一次,忙等
    • 而计算机,1ms就可以看很多很多次~
  • 那么我们只需要在schedule时唤醒一下,让他才判断一次就行了~

    • 这防止新插入的任务更早而被忽略
  • 大大减少判断次数!

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 最终版:
public void schedule(Runnable runnable, long delay) {
    MyTask myTask = new MyTask(runnable, delay);
    tasks.put(myTask);
    synchronized (locker) {
        locker.notify();
    }
}

private Object locker = new Object();

public MyTimer() {
    Thread t = new Thread(() -> {
        while(true) {
            synchronized (locker) {
                try {
                    MyTask myTask = tasks.take();
                    long nowTime = System.currentTimeMillis();
                    if(myTask.time <= nowTime) {
                        myTask.runnable.run();
                    }else {
                        tasks.put(myTask);
                        locker.wait(myTask.time - nowTime);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });
    t.start();
}

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 别忘了启动线程~

1.3 测试MyTimer

  • 用MyTimer替换之前的Timer

  • TimeTask也可替换为Runnable,不过没关系,向上转型~

public static void main(String[] args) {
    MyTimer timer = new MyTimer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("星期一好耶 ^ v ^");
        }
    },1000);
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("星期二好耶 ^ v ^");
        }
    },2000);
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("星期三好耶 ^ v ^");
        }
    },3000);
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("星期四好耶 ^ v ^");
        }
    },4000);
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("星期五好耶 ^ v ^");
        }
    },5000);
    System.out.println("今天不好耶 T . T");
}
  • 测试结果正常:

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 退出代码130,是按ctrl + f2

1.4 补充

  • 你可能也发现了,代码之中并没有完全保证,一个线程一定会在规定的时间后执行
  • 因为一个定时器,只能运行一个线程,没有并发性
    • 只是和main线程并发~
    • 所以,如果一个线程运行时间较长,会导致其后的任务“被迫延时”
    • 而判断条件不是等于等于,也有这一方面原因
      • 另一方面原因是,可能因为调度问题有误差~

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 此时这个定时器,就只能起到,保证任务执行顺序的功能~
1.4.1 例子1
  • 例如以下测试代码:
 public static void main(String[] args) {
        MyTimer timer = new MyTimer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("星期一好耶 ^ v ^");
                try {
                    Thread.sleep(5000);
                    System.out.println("已过去五秒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },1000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("星期二好耶 ^ v ^");
            }
        },2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("星期三好耶 ^ v ^");
            }
        },3000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("星期四好耶 ^ v ^");
            }
        },4000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("星期五好耶 ^ v ^");
            }
        },5000);
        System.out.println("今天不好耶 T . T");
    }
}

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 第一个任务要花5秒,而还差1秒,第二个任务就应该启动~
  • 而现象是这样的:

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 后面的任务已经受严重延迟~
1.4.2 例子2
  • 如果一个任务死循环了,会导致后面的任务无限延期

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 就会导致下面这种情况:

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

注意:

  • 这并不是我写的定时器有问题 ,Java标准库的定时器,就是这样子的, 一个定时器一个时间段里只能执行一个任务
    • 现象跟MyTimer是一样的
    • 就是这两个例子那样
  • 一个任务时间太长,会导致下一个任务延迟
  • 只起“区分先后”的作用

1.5 顺带一题

问:wait的同步锁的位置不同,结果会怎么样?

  • 例如:

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 这两种锁的框法不同,结果一样吗?
1.5.1 后者

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 重点就在于,没有保证take与wait是原子的~
1.5.2 前者
  • 保证原子性后:

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

2. 线程池

  • 跟字符串常量池和数据库连接池一样
    • 这个池的作用就提高效率,节省开销~
    • 即使线程很轻量,但是积少成多就不能忽略~
  • 只要再池子里去拿,就要比从系统申请要快~

提高效率还能提高轻量化线程“协程”,Java标准库还不支持

而线程池是一个重要的途径~

  • 从线程池里拿线程,纯纯的用户态操作
  • 而从系统上申请,就必须设计用户态和内核态之间的切换
    • 真正的创建线程,是在内核态完成的

2.1 用户态和内核态

  • 操作系统 = 内核 + 配套的应用程序
    • 内核:各种系统管理和驱动,而内核就是为了支持应用程序的
    • 这里不仅仅指核心~
      • 因为进程管理这是他的工作之一
      • 逻辑核心们也只是他的打工人~

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 需要内核支持,才能运行的应用程序~

    • 例如,println,打印到屏幕,需要通过硬件管理~
  • 即,内核给那么多人服务,那么就不一定及时

举个栗子:

  • 去银行打印资料,前台可以帮你打印
    • 而前台在同时会去帮助其他人,给你打印好了还要好一会儿才给你~

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 你也可以去自助打印机打印
    • 这样的时间消耗就只会缩短在 “打印需求” 内去消耗

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 也就是说,我们在申请线程时

    • 内核态申请 ==> 内核要顾及进程管理和其他管理与驱动~

    • JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

    • 用户态去拿 ==> 只需要在进程管理这个单项里去拿线程~

    • JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 当然,线程的诞生,还是要内核态申请

    • 放进线程池,之后在线程池里用户态拿就好~

2.2 标准库线程池类ExecutorService

  • Java标准库实现了一个接口,ExecutorService,在进程中服务线程执行~

    • 通过这个池的服务,不需要每次都申请~
  • 但是这个接口不是通过new子类对象去实例化的,而是用一个静态方法去实例化~

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 而这里的Executors类就是“工厂类”
    • 这个类就是为了构造“线程池”而存在的
    • 这个类可以调用各种静态方法
      • 而这些静态方法使用起来简单
      • 并且可以构造各种满足我们特殊需要的对象

2.3 工厂模式

  • “工厂”

    • 即“对象工厂”,可以工厂生产出不同的对象
    • 有员工去帮你生产,使用简单
      • 降低使用成本
    • 相同原料可以有不同产品,避免参数列表相同导致无法触发重载
      • 重要作用!
  • 而工厂模式其实就是,把一个类/接口的构造方法,交给一个“工厂类”去定义

    • 即,将构造方法打包成类

Executors工厂

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 重点掌握

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

你也可以自己“开个厂”

  • 就比如说,一个【堆】,泛型类是我们的自定义类
  • 而我们的自定义类要我们去规定比较方法
public class A {
    int a1;
    int a2;
    int a3;
    int a4;
    int a5;
    int a6;
}
  • 假设我们A类有六个成员(都是int类型)
    • 要求建立6个堆,每个堆以不同的比较规则去创建
      • 每次创建都好麻烦,都要写个比较器~
  • 只需要“开个比较器厂”,把这些构造方法包装起来就好~
    • 以后构造的时候,通过不同的方法名调用对应的构造方法~
    • 比较器Comparator
      • 构造方法基本都没有参数列表的,那么就不能用重载去解决~
        • 比较器的不同主要不是因为构造方法,而是compare被怎么重写有关~
      • compare方法重写也只能重写一个

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

2.3.1 开[A的构造厂]
    public static A createA1(int a) {
        //匿名内部类优先捕获全局性质变量,这里在代码块内,a1就为全局性变量~
        return new A() {
            {
                this.a1 = a;
            }
        };
    }
    public static A createA2(int a) {
        return new A() {
            {
                this.a2 = a;
            }
        };
    }
    public static A createA3(int a) {
        return new A() {
            {
                this.a3 = a;
            }
        };
    }
    public static A createA4(int a) {
        return new A() {
            {
                this.a4 = a;
            }
        };
    }
    public static A createA5(int a) {
        return new A() {
            {
                this.a5 = a;
            }
        };
    }
    public static A createA6(int a) {
        return new A() {
            {
                this.a6 = a;
            }
        };
    }
}

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

2.3.2 开[A的比较器厂]
class CreateComparatorA {
    public static Comparator<A> createA1() {
        return ((o1, o2) -> {
           return o1.a1 - o2.a1;
        });
    }
    public static Comparator<A> createA2() {
        return ((o1, o2) -> {
           return o1.a2 - o2.a2;
        });
    }
    public static Comparator<A> createA3() {
        return ((o1, o2) -> {
           return o1.a3 - o2.a3;
        });
    }
    public static Comparator<A> createA4() {
        return ((o1, o2) -> {
           return o1.a4 - o2.a4;
        });
    }
    public static Comparator<A> createA5() {
        return ((o1, o2) -> {
           return o1.a5 - o2.a5;
        });
    }
    public static Comparator<A> createA6() {
        return ((o1, o2) -> {
           return o1.a6 - o2.a6;
        });
    }
}

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

2.3.3 测试
public class A {
    int a1;
    int a2;
    int a3;
    int a4;
    int a5;
    int a6;
    //参数列表相同无法特定构造特定成员~

    @Override
    public String toString() {
        return "A{" +
                "a1=" + a1 +
                ", a2=" + a2 +
                ", a3=" + a3 +
                ", a4=" + a4 +
                ", a5=" + a5 +
                ", a6=" + a6 +
                '}' + '\n';
    }

    public static void main(String[] args) {
        PriorityQueue<A> queue1 = new PriorityQueue<>(CreateComparatorA.createA1());
        PriorityQueue<A> queue2 = new PriorityQueue<>(CreateComparatorA.createA2());
        PriorityQueue<A> queue3 = new PriorityQueue<>(CreateComparatorA.createA3());
        PriorityQueue<A> queue4 = new PriorityQueue<>(CreateComparatorA.createA4());
        PriorityQueue<A> queue5 = new PriorityQueue<>(CreateComparatorA.createA5());
        PriorityQueue<A> queue6 = new PriorityQueue<>(CreateComparatorA.createA6());

        queue1.offer(createA.createA1(2));
        queue1.offer(createA.createA1(1));
        queue1.offer(createA.createA1(4));
        queue1.offer(createA.createA1(3));
        queue1.offer(createA.createA1(5));

        System.out.println(queue1);

    }
}
  • 结果:
    • 确实以a1为标准~

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

当然,工厂当然不只可以生产构造方法:

  • 还能生产那些我们需要的:重复参数列表的方法
  • 例如生产A的toString()方法~
  • 不额外说了~

2.4 ExecutorService的属性和方法

2.4.1 通过工厂类构造

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 不给固定容量,按需创建线程池~

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 跟定时器有关~

最重点的一个:

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 提供固定容量的线程池构造方法~

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

2.4.2 submit方法
  • 提交线程~
public static void main(String[] args) {
    ExecutorService pool = Executors.newFixedThreadPool(10);
    pool.submit(() -> {
        System.out.println("好耶 ^ v ^ ");
    });
}
2.4.3 ThreadPoolExecutor类的属性
  • 既然是构造ThreadPoolExecutor
  • 那么它的属性就至关重要~

下面是Java的官方文档的内容:

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 这个类在【util应用应用工具】的【concurrent并发包】中~
    • 简称【JUC

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 通过这个构造方法让我们看到很多属性~
    • 我也只讲解这几个属性~
    • 也不讲这个方法咋实现的
  1. corePoolSizemaximumPoolSize
  • corePoolSize为核心线程数
  • maximumPoolSize为最大线程数
    • 核心线程,即不能随意停止的线程
    • 最大线程数里【包含核心线程和“临时线程”】
      • 这个“临时线程”就相当于公司里的实习生
        • 关键时期来应急
      • 这个核心线程就相当于正式员工
        • 不能随意辞退

线程池会在任务少的空闲期,根据这些参数进行线程调整,把一些临时线程给销毁了~

  1. keepAliveTime(long)unit(TimeUnit)
  • keepAliveTime 为临时线程存活时间~

    • “实习生”并不是立即被辞退
    • 而是跟这个参数有关
    • 允许最多活多久~
  • unit ==> 时间单位

  1. BlockingQueue< Runnable > workQueue
  • 线程池要管理很多任务
  • 通过阻塞队列来组织~
    • 方便程序员控制线程数据交互
    • submit提交到这个阻塞队列里~
  1. ThreadFactory threadFactory
  • 线程工厂 ,跟工厂模式有关~
  • 不细讲
  1. RejectedExecutionHandler handler
  • 线程池的拒绝执行应对策略~
  • 池子满了,继续往里添加线程,如何应对?如何拒绝?
    • 线程池满了是不依赖阻塞队列的
      • 这个任务要不要干最好立马给出决策!
    • 一般是空了依赖阻塞队列~
    • 还有阻塞队列的线程安全性和解耦合性也很好
2.4.4 线程池的拒绝策略

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  1. 直接抛异常 ---- 毁灭
    • 哇哇大哭
  2. 直接拒绝 ---- 谁给我这个线程任务,谁自己去完成
    • 达咩达咩
  3. 抛弃最老任务 ---- 把最先安排的任务 [队列头] 给删了,替换成新任务
    • 做出牺牲
  4. 抛弃最新任务 ---- 我继续干原来的活,新的活谁都没干
    • 原封不动

2.5 模拟实现线程池

public class MyThreadPool {
    private BlockingQueue<Runnable> pool = new LinkedBlockingQueue<>();

    public void submit(Runnable runnable) throws InterruptedException {
        pool.put(runnable);
    }

    //实现固定线程数的线程池
    //不是容量,是确确实实的线程数
    
    public MyThreadPool(int number) {
        for (int i = 0; i < number; i++) {
            Thread thread = new Thread(() -> {
                Runnable runnable = null;
                try {
                    while(true) {
                    	runnable = pool.take();
                    	runnable.run();  
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
        }
    }
}
  • 我们的简单实现,不涉及2.4.3的属性~

  • 注意:这里的线程数,是工作人数,定量

    • 而阻塞队列里的线程数,则是这些人做的”任务“

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

  • 测试:
public static void main(String[] args) throws InterruptedException {
    MyThreadPool myThreadPool = new MyThreadPool(10);
    for (int i = 1; i <= 1000; i++) {
        int id = i; //线程id,变量捕获~
        myThreadPool.submit(() -> {
            System.out.println("好耶^ v ^ " + id);
        });
    }
  • 提供固定的工作人员 * 10
  • 源源不断塞1000个任务~
  • 工作人员疯抢~

JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式
——

  • 数据顺序无序很正常
    • 线程调度无序嘛~
  • 程序还未结束
    • 这是因为十个工作人员“吸血鬼”还等着任务呢~

线程池中如何体现,“用户态拿”:

  1. 线程池中有固定数量的线程,而这些线程是一开始一次性申请的
  2. 之后我们无需为了一个或者多个任务再去额外申请一个Thread对象了
  3. 只需要提交任务给线程池,让线程池里的“空线程”去帮我们干事
  4. 反复用这些空线程去做任务~
    • 只需要提交就行,不需要从线程池里取线程
    • 我们可能没有拿线程这个操作~
    • 但是这样就相当于我们拿了线程做了任务~
      • 只不过这个任务几乎全自动地被线程池帮忙在一个线程里执行了
      • 而原本执行一个任务就要我们需要自己申请个新的Thread对象
        JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式

注意:

  • 线程 不等于 任务
  • 任务必须依托线程才能执行
    ——

2.6 线程池的固定线程数的确定(理论)

  • 至于线程池固定线程数,设置为多少合适?

    • 最好最科学的方式就是去测试
  • cpu密集型,主要做一些计算工作,要在cpu上运行~

  • IO密集型,主要等待一些IO操作(读写硬盘/读写网卡),不怎么吃cpu

    • 如果你的线程全是使用cpu的,那就得设置线程数少于核心数~
    • 如果全是使用IO的,那就可以设置很多很多线程,远超核心数
  • 而实际情况不会这么极端,所以这个线程数一定是要看实际情况的

    • 所以就要测试!
    • 通过一些数据去看看哪个固定线程数是OK的
      • 例如执行时间…检测资源使用状态~
      • 控制变量法~


文章到此结束!谢谢观看 !
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆!

多线程初阶已经结束~ 后续会出线程进阶的博客!

敬请期待吧~文章来源地址https://www.toymoban.com/news/detail-417818.html



到了这里,关于JavaEE & 线程案例 & 定时器 & 线程池 and 工厂模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【JavaEE初阶】多线程(四)阻塞队列 定时器 线程池

    概念 阻塞队列是一种特殊的队列. 也遵守 “ 先进先出 ” 的原则. 阻塞队列能是一种线程安全的数据结构, 并且具有以下特性: 当队列满的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素. 当队列空的时候, 继续出队列也会阻塞,直到有其他线程往队列中插入元素

    2023年04月26日
    浏览(54)
  • 多线程案例(3)-定时器

    大家好,我是晓星航。今天为大家带来的是 多线程案例三 相关的讲解!😀 定时器是什么 定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定 好的代码. 定时器是一种实际开发中非常常用的组件. 比如网络通信中, 如果对方

    2024年02月14日
    浏览(42)
  • Java多线程案例之定时器

    定时器是一种实际开发中非常常用的组件, 类似于一个 “闹钟”, 达到一个设定的时间之后, 就执行某个指定好的代码. 比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连. 比如一个 Map, 希望里面的某个 key 在 3s 之后过期(自动删除). 类似于这样的场景就需要

    2024年02月20日
    浏览(49)
  • 【Java | 多线程案例】定时器的实现

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习JavaEE的一点学习心得,欢迎大家在评论区交流讨论💌 Java中, Timer类 是用于计划和执行重复任务的类( Java标准

    2024年02月03日
    浏览(43)
  • Java 多线程系列Ⅳ(单例模式+阻塞式队列+定时器+线程池)

    设计模式就是软件开发中的“棋谱”,软件开发中也有很多常见的 “问题场景”。针对这些问题场景,大佬们总结出了一些固定的套路。按照这些套路来实现代码可能不会很好,但至少不会很差。当前阶段我们需要掌握两种设计模式: (1)单例模式 (2)工厂模式 概念/特征

    2024年02月09日
    浏览(57)
  • 【JavaEE初阶】 定时器详解与实现

    定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码 定时器是一种实际开发中非常常用的组件. 比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连. 比如一个 Map, 希望里面的某个 key 在 3s 之后过

    2024年02月06日
    浏览(38)
  • 【多线程】定时器,详解定时器原理,让大家更深刻的理解多线程

    前言: 大家好,我是 良辰丫 ,今天我们一起了解一下定时器,通过定时器来熟悉一下线程安全等相关知识点.💞💞💞 🧑个人主页:良辰针不戳 📖所属专栏:javaEE初阶 🍎励志语句:生活也许会让我们遍体鳞伤,但最终这些伤口会成为我们一辈子的财富。 💦期待大家三连,关注

    2024年02月01日
    浏览(60)
  • 多线程---定时器

    定时器的功能和“闹钟”类似,代码中的定时器通常都是“多长时间之后,执行某个动作”。 注: 一个定时器可以安排多个任务。 调用schedule安排任务时,一定要重写run方法,明确任务内容 定时器开启后不会自动结束,得手动杀死进程 版本一:实现简单的定时器 schedule方法

    2024年02月08日
    浏览(39)
  • java多线程之定时器

    定时功能在java中主要是通过 Timer 类实现,因为它在内部还是使用多线程的方式进行处理,所以和多线程技术还是有非常大的管理 在JDK库中Timer类主要负责计划任务的功能,也就是在指定时间开始执行某一个任务,Timer类的主要功能就是设置计划任务,封装任务的类确是Timer

    2024年02月05日
    浏览(47)
  • 51-定时器与按键控制LED流水灯模式&定时器时钟

    按键(以独立按键为例)控制LED流水灯模式: 在按键控制LED流水灯模式中,如果仅仅简单的把独立按键与LED流水灯拼接起来,则会出现一些问题:在LED流水灯的代码中会有长时间的Delay,此时按键检测就会很不灵敏:按下时不灵敏,需要一些时间才能被检测到,按下后再松手时

    2024年02月08日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包