目次
タイマー:
タイマーとは
タイマーは、ソフトウェア開発においても重要なコンポーネントです。「目覚まし時計」に似ています。設定された時間になると、指定されたコードが実行されます。これは、私たちが普段話している目覚まし時計と非常に似ていますが、リマインダーとして機能するだけでなく、実際にそれを行うこともできます. また、ネットワーク プログラミングでは、タイマーの用途がまだたくさんあります。たとえば、ウェブページにアクセスしてタイマーを設定すると、訪問時間が長すぎるとタイマーがトリガーされ、訪問が終了し、時間内に接続が切断され、ブロックされたり待機したりすることはなくなります。
標準ライブラリのタイマー:
標準ライブラリには Timer クラスが用意されており、この Timer を使用して定期的にやりたいことを実行できます。
Timer クラスのコア メソッドは、2 つのパラメーターを含むスケジュールです。最初のパラメーターは実行するタスク コードを指定し、2 番目のパラメーターはタスクを実行する時間をミリ秒単位で指定します。
import java.util.Timer;
import java.util.TimerTask;
public class ThreadDemo1 {
public static void main(String[] args) {
//设置定时器
Timer timer=new Timer();
System.out.println("已经设置好定时器!!!");
//指定定时器的任务和执行时间
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("执行任务1!!");
}
},1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("执行任务2!!");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("执行任务3!!");
}
},3000);
}
}
タイマーを実装します。
//实现一个定时器
import java.util.concurrent.PriorityBlockingQueue;
/**
* 实现定时器的核心:
* 1、定时器需要让任务有一个放置的地方(考虑使用优先级阻塞队列,可以实现线程安全和优先级的情况)
* 2、定时器需要让任务在指定的时间进行执行(考虑使用一个线程不断进行扫描来进行判断)
*/
//任务
class MyRunnable implements Comparable<MyRunnable>{
//任务使用Runnable创建
private Runnable runnable;
//推迟时间(单位:ms)
private long time;
//构造方法
public MyRunnable(Runnable runnable,long time){
this.runnable=runnable;
this.time=time;
}
public Runnable getRunnable() {
return runnable;
}
public long getTime() {
return time;
}
//执行任务
public void run(){
runnable.run();
}
//重写
@Override
public int compareTo(MyRunnable o) {
return (int)(this.time-o.time);
}
}
class MyTimer{
//使用object来控制阻塞队列执行顺序
private Object loker=new Object();
//需要一个优先级阻塞队列
PriorityBlockingQueue<MyRunnable>queue=new PriorityBlockingQueue<>();
//一个线程用来扫描队列中的任务(需要在构造方法中实现)
public MyTimer(){
Thread t=new Thread(()->{
while(true){
try {
synchronized (loker){
MyRunnable task=queue.take();
//记录当前的时间戳
long curTime=System.currentTimeMillis();
//判断是否到达执行时间
//如果没有到达执行时间就把任务再放回阻塞队列当中
if(curTime< task.getTime()){
queue.put(task);
//进行阻塞
loker.wait(task.getTime()-curTime);
//如果到达执行时间则直接执行任务
}else{
task.run();
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
}
public void schedule(Runnable runnable,long after){
MyRunnable runnable1=new MyRunnable(runnable,System.currentTimeMillis()+after);
queue.put(runnable1);
synchronized (loker){
loker.notify();
}
}
}
public class MyTask {
public static void main(String[] args) {
MyTimer timer1=new MyTimer();
timer1.schedule(new Runnable() {
@Override
public void run() {
System.out.println("到达任务1执行时间,执行任务1!");
}
},1000);
MyTimer timer2=new MyTimer();
timer2.schedule(new Runnable() {
@Override
public void run() {
System.out.println("到达任务2执行时间,执行任务2!");
}
},3000);
}
}
スレッド プール:
スレッドプールとは何ですか?
スレッド プールに関して言えば、文字列定数プールなど、これまでに学んだ多くの同様の「プール」を考えることができるはずです。スレッドの作成と破棄のオーバーヘッドはプロセスのオーバーヘッドよりも小さいと言えますが、作成と破棄のたびに多くのリソースが消費されることは否定できません。(どれだけ相対的に言えばよいか)、オーバーヘッドを小さくするために、スレッド プールを導入しました。
スレッド プールの最大の利点は、毎回スレッドを開始および破棄するオーバーヘッドです。
想像してみてください、現在 10 個のスレッドがありますが、それらを一度作成してスレッド プールに入れ、バックアップ用のリソースの消費を減らしますか、それともオーバーヘッドを抑えて 1 つずつ作成しますか? 答えはもちろん、一度に 10 個、1 つずつ作成し、そのたびにリソースを消費し、破棄するたびにリソースを消費することであり、一度に複数作成するとオーバーヘッドが大幅に削減されます。
標準ライブラリのスレッド プール:
標準ライブラリのスレッド プールは、Executors.newFixedThreadPool(10)を使用して、固定数 10 スレッドのスレッド プールを作成します。
戻り値の型はExecutorServiceで、ExecutorService.submitでタスクをスレッドプールに登録できます 。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool1 {
public static void main(String[] args) {
//在线程池中创建10个线程
ExecutorService pool= Executors.newFixedThreadPool(10);
for (int i = 1; i <=10; i++) {
int n=i;
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("线程池中执行任务"+n);
}
});
}
}
}
ここで使用する Executors クラスの static メソッドは、new 操作を使用せずにオブジェクトを直接構築します. クラスの static メソッドを使用してオブジェクトを構築することは、静的メソッドで new 操作を隠すことと同じです. 次に、submit メソッドを使用して、タスクを Runnable インターフェイスの形式でスレッド プールに送信します。
Executor は、基本的に ThreadPoolExecutor クラスのラッパーです。
工場パターン:
これがデザインパターンです -工場パターン
新しい操作を静的メソッド内に隠すメソッドがファクトリ メソッドです。ファクトリメソッドを提供するクラスをファクトリクラスと呼びます。このデザインパターンをファクトリーパターンと呼びます。ファクトリ パターンの利点は何ですか? デザイン パターンは主にいくつかの文法上の穴を埋めるためのものであり、ファクトリ パターンは主に構築方法の穴を埋めるためのものであり、オブジェクトを作成するときに作成ロジックをクライアントに公開しないことがわかっています。例えば座標系を作成する場合、直交座標系と極座標系の2種類があり、一般的にパラメータは2倍になりますので、このときオーバーロードで作成を完了させようとするとうまくいきません。構築方法を使用する必要があります。
上記は単純な使用法の 1 つです。スレッド プールを作成するには、次のような多くの方法があります。
同時に、メイン スレッドが終了しても、プログラムは停止しないことに注意してください。これは、スレッド プール内のスレッドがフォアグラウンド スレッドに属しているため、プロセスが終了しないためです。同時に、上記のタイマーのタスクもフォアグラウンド スレッドであることに注意してください。この時点で、停止する場合は、上の停止ボタンをクリックする必要があります。
スレッドプールの実装:
1. コア操作はサブミットです。これにより、タスクがスレッド プールに追加されます。
2. Runnable を使用してタスクを記述します。
3. BlockingQueue を使用して、すべてのタスクを整理します。
4. 各スレッドがしなければならないこと: BlockingQueue からタスクを取得して実行し続けます。
5. スレッド プール内のスレッドの最大数 n を指定します。現在のスレッド数がこの最大値を超えると、新しいスレッドは追加されません。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class MyThreadPool{
//阻塞队列当中放入任务
private BlockingQueue<Runnable> queue=new LinkedBlockingQueue<>();
//构造方法中创建出工作的线程
public MyThreadPool(int n){
for (int i = 0; i < n; i++) {
//创建线程
Thread t=new Thread(()->{
while(true){
//任务为空
Runnable runnable= null;
try {
//将阻塞队列中的任务拿出给runnable
runnable = queue.take();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//执行任务
runnable.run();
}
});
t.start();
}
}
//把任务放到阻塞队列中(注册任务)
public void submit(Runnable runnable){
try {
queue.put(runnable);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public class ThreadPool2 {
public static void main(String[] args) {
//创建一个带有10个线程的线程池
MyThreadPool myThreadPool=new MyThreadPool(10);
for (int i = 0; i <10 ; i++) {
int n=i+1;
myThreadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println("执行线程池中任务:"+n);
}
});
}
}
}