主頁 >  其他 > Java的多執行緒安全問題--API檔案深入研究3.0

Java的多執行緒安全問題--API檔案深入研究3.0

2021-12-31 08:43:36 其他

目錄

Thread

改進后的電影院售票出現問題

Thread(Runnable target, String name)

改進電影院售票1

解決執行緒安全問題的基本思想

解決執行緒同步安全問題的第一種方法

改進電影院售票2

解決執行緒同步安全問題的第二種解法:加Lock鎖

Class ReentrantLock

void lock() 加鎖

void unlock() 釋放鎖

改進電影院售票3

執行緒同步案例:共享資料案例(執行緒通信)

死鎖

執行緒組

執行緒池Executors

newFixedThreadPool

創建執行緒的第三種方式

callable

匿名內部類方式使用多執行緒

定時器

Timer


Thread

改進后的電影院售票出現問題

  • 問題

相同的票出現多次
CPU的一次操作必須是原子性的
還出現了負數的票
隨機性和延遲導致的

  • 注意

執行緒安全問題在理想狀態下,不容易出現,但一旦出現對軟體的影響是非常大的,

Thread(Runnable target, String name)

查看API檔案我們知道:

public Thread(Runnable target,String name)

分配一個新的Thread物件, 此構造具有相同的效果Thread (null, target, name)

引數

target - 啟動此執行緒時呼叫其run方法的物件, 如果null ,則呼叫此執行緒的run方法,

name - 新執行緒的名稱

改進電影院售票1

創建TicketWindow1類:

public class TicketWindow1 implements Runnable{
    //定義100張票
    private int tickets = 100;

    private Object obj = new Object();

    @Override
    public void run() {
        while (true){
            synchronized (obj){
                if (tickets > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
                }
            }
        }
            }

}

創建SellTicketDemo1實作類:

public class SellTicketDemo1 {
    public static void main(String[] args) {

        TicketWindow1 ticketWindow1 = new TicketWindow1();

        //創建執行緒物件模擬3個視窗,并給執行緒起名字
        Thread t1 = new Thread(ticketWindow1, "視窗1");
        Thread t2 = new Thread(ticketWindow1, "視窗2");
        Thread t3 = new Thread(ticketWindow1, "視窗3");

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

運行結果:

由于博主的CPU太過強大,多次嘗試后這里沒有出現預想的結果,


解決執行緒安全問題的基本思想

首先想為什么出現問題?(也是我們判斷是否有問題的標準)

  • 是否是多執行緒環境
  • 是否有共享資料
  • 是否有多條陳述句操作共享資料

如何解決多執行緒安全問題呢?

  • 基本思想:讓程式沒有安全問題的環境
  • 怎么實作呢?

把多個陳述句操作共享資料的代碼給鎖起來,讓任意時刻只能有一個執行緒執行即可,

同步代碼塊

格式:
synchronized(物件){

需要同步的代碼;

}
同步可以解決安全問題的根本原因就在那個物件上,該物件如同鎖的功能,

同步代碼塊的鎖物件是誰呢?

任意物件,但是多個執行緒之間鎖物件要一樣

同步方法的鎖物件是誰呢?

將synchronized關鍵字放到方法上
同步方法的鎖物件是this
靜態方法的鎖物件是誰呢?

class檔案,位元組碼檔案物件也是屬于一個Object類下面的物件,這個class檔案不能是隨便一個類的位元組碼檔案應該是run方法所在類的位元組碼檔案

解決執行緒同步安全問題的第一種方法

改進電影院售票2

創建TicketWindow2類:

public class TicketWindow2 implements Runnable {
    //定義1000張票
    private static  int tickets = 1000;
    int i = 1;
    @Override
    public void run() {
        while (true){
            if (i%2==0){
                synchronized (TicketWindow2.class){
                    if (tickets > 0){
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
                    }
                }
            }else {
                sellTicket();
            }
        }
    }

    private synchronized static void sellTicket() {
        if (tickets > 0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");

        }
    }
}

創建SellTicketDemo2實作類:

public class SellTicketDemo2 {
    public static void main(String[] args) {

        TicketWindow2 ticketWindow2 = new TicketWindow2();

        Thread t1 = new Thread(ticketWindow2, "視窗1");
        Thread t2 = new Thread(ticketWindow2, "視窗2");
        Thread t3 = new Thread(ticketWindow2, "視窗3");

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

    }
}

運行結果:

從運行結果上來看我們實作了執行緒的安全,


解決執行緒同步安全問題的第二種解法:加Lock鎖

Class ReentrantLock

查看API檔案我們知道:

public class ReentrantLock
extends Object
implements Lock, Serializable

一個可重入互斥Lock具有與使用synchronized方法和陳述句訪問的隱式監視鎖相同的基本行為和語意,但具有擴展功能,

A ReentrantLock由執行緒擁有 ,最后成功鎖定,但尚未解鎖, 呼叫lock的執行緒將回傳,成功獲取鎖,當鎖不是由另一個執行緒擁有, 如果當前執行緒已經擁有該鎖,該方法將立即回傳, 這可以使用方法isHeldByCurrentThread()getHoldCount()進行檢查,

該類的建構式接受可選的公平引數, 當設定true ,在爭用下,鎖有利于授予訪問最長等待的執行緒, 否則,該鎖不保證任何特定的訪問順序, 使用許多執行緒訪問的公平鎖的程式可能會比使用默認設定的整體吞吐量(即,更慢,通常要慢得多),但是具有更小的差異來獲得鎖定并保證缺乏饑餓, 但是請注意,鎖的公平性不能保證執行緒調度的公平性, 因此,使用公平鎖的許多執行緒之一可以連續獲得多次,而其他活動執行緒不進行而不是當前持有鎖, 另請注意, 未定義的tryLock()方法不符合公平性設定, 如果鎖可用,即使其他執行緒正在等待,它也會成功,

建議的做法是始終立即跟隨locktry塊的通話,最常見的是在之前/之后的建設,如:

   class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } } } 

除了實作Lock介面,這個類定義了許多publicprotected方法用于檢查鎖的狀態, 其中一些方法僅適用于儀器和監控,

此類的序列化與內置鎖的操作方式相同:反序列化鎖處于未鎖定狀態,無論其序列化時的狀態如何,

此鎖最多支持同一個執行緒的2147483647遞回鎖, 嘗試超過此限制會導致Error從鎖定方法中拋出,

void lock() 加鎖

查看API檔案我們知道:

public void lock()

獲得鎖,

如果鎖沒有被另一個執行緒占用并且立即回傳,則將鎖定計數設定為1,

如果當前執行緒已經保持鎖定,則保持計數增加1,該方法立即回傳,

如果鎖被另一個執行緒保持,則當前執行緒將被禁用以進行執行緒調度,并且在鎖定已被獲取之前處于休眠狀態,此時鎖定保持計數被設定為1,

Specified by:

lock在界面 Lock

void unlock() 釋放鎖

查看API檔案我們知道:

public void unlock()

嘗試釋放此鎖,

如果當前執行緒是該鎖的持有者,則保持計數遞減, 如果保持計數現在為零,則鎖定被釋放, 如果當前執行緒不是該鎖的持有者,則拋出IllegalMonitorStateException

Specified by:

unlock在界面 Lock

例外

IllegalMonitorStateException - 如果當前執行緒不持有此鎖

在此之前我們解決執行緒同步安全問題的時候,使用的是synchronized關鍵字,通過分析然后將哪些代碼塊給包起來,但是我們并沒有直接看到在哪里上了鎖,或者說在哪里釋放了鎖讓其它執行緒獲取到,為了更加清晰的表達如何加鎖以及如何釋放鎖,JDK1.5之后提供了一個新的鎖物件Lock,

改進電影院售票3

創建TicketWindow3類:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TicketWindow3 implements Runnable {
    //定義100張票
    private int tickets = 100;
    
    //創建鎖物件
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            lock.lock();
            if (tickets > 0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "正在售出第" + (tickets--) + "張票");
            }
            lock.unlock();

        }
    }
}

創建SellTicketDemo3實作類:

public class SellTicketDemo3 {
    public static void main(String[] args) {
        TicketWindow3 ticketWindow3 = new TicketWindow3();
        Thread t1 = new Thread(ticketWindow3, "視窗1");
        Thread t2 = new Thread(ticketWindow3, "視窗2");
        Thread t3 = new Thread(ticketWindow3, "視窗3");

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

運行結果:


執行緒同步案例:共享資料案例(執行緒通信)

創建Student類:

public class Student {
    String name;
    int age;
    boolean flag;
}

創建自定義類GetThread:

import java.util.concurrent.locks.Lock;

public class GetThread implements Runnable {
    private Student s;
    private Lock lock;


    public GetThread(Student student) {
        this.s = student;
    }

    public GetThread(Student student, Lock lock) {
        this.s = student;
        this.lock = lock;
    }

    @Override
    public void run() {
        while (true){
            synchronized (s){
                //判斷學生物件有沒有值
                //如果flag的值是false,說明沒有資料,消費者進if判斷等待
                if(!s.flag){
                    try {
                        s.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println(s.name + "---" + s.age);

                //消費者消費完資料后通知生產者生產資料
                s.notify();
                s.flag = false;
            }
        }
    }


}

創建自定義類SetThread類:

import java.util.concurrent.locks.Lock;

public class SetThread implements Runnable{
    private Student s;
    private int i = 0;
    private Lock lock;


    public SetThread(Student student) {
        this.s = student;
    }

    public SetThread(Student student, Lock lock) {
        this.s = student;
        this.lock = lock;
    }

    @Override
    public void run() {
        while (true){
            synchronized (s){
                //判斷學生物件有沒有值
                //flag初始的時候是flase,表示沒有值,如果是true表示學生物件有值
                //有值對于生產者來說,等待消費者消費
                if(s.flag){
                    try {
                        s.wait();  //呼叫wait(),執行緒阻塞
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                if(i%2==0){
                    s.name = "劉德華";
                    s.age = 65;
                }else {
                    s.name = "張學友";
                    s.age = 73;
                }
                i++;

                //生產者生產完資料之后通知消費者消費資料
                s.notify();
                s.flag = true;

            }

        }
    }
}

創建實作類:StudentDemo

public class StudentDemo {
    public static void main(String[] args) {
        //創建共享的學生物件
        Student student = new Student();

        //創建自定義類物件
        SetThread setThread = new SetThread(student);
        GetThread getThread = new GetThread(student);

        //根據Runnable的物件創建生產者和消費者的執行緒物件
        Thread proThread = new Thread(setThread);
        Thread cusThread = new Thread(getThread);

        cusThread.start();
        proThread.start();
    }
}

運行結果:

分析:

    共享資料:Student
    生產者:SetThread 給學生的成員變數進行賦值操作
    消費者:GetThread 獲取學生的成員變數的資訊
    測驗類:StudentDemo 創建執行緒并測驗

在寫這個案例之前我們將會遇到一些問題可能是以下原因導致的

CPU一點點的時間片就足矣執行很多次

執行緒執行的時候具有隨機性導致的

注意事項:
    1、不同種類的執行緒類都要加鎖
    2、不用種類的執行緒類中的鎖物件要一樣

問題3:雖然我們解決執行緒安全問題,但是經過分析,程式還存在著等待喚醒機制的問題,
    加入等待喚醒機制,

    如何添加等待喚醒機制呢?
        Object類中有三個方法:
            void wait() 導致當前執行緒等待,直到另一個執行緒呼叫該物件的 notify()方法或 notifyAll()方法,
            void notify() 喚醒正在等待物件監視器的單個執行緒,
            void notifyAll() 喚醒正在等待物件監視器的所有執行緒,
    這三個方法為什么不定義再Thread類中呢?
        這些方法想要呼叫,必須通過鎖的物件呼叫,而我們直到同步代碼塊的鎖物件可以是任意物件
        所以這些方法都在Object類中,因為java中所有類的父類都是Object

既然實作了執行緒的共享,那么如果出現了同步嵌套,就容易產生死鎖問題

死鎖

死鎖是指兩個或者兩個以上的執行緒在執行的程序中,因爭奪資源產生的一種互相等待現象

參考代碼:

創建DieLock類:

public class DieLock extends Thread {
    private boolean flag;

    public DieLock(boolean flag){
        this.flag = flag;
    }

    /*  現象1:
            if lock1
            else lock2

        現象2:
            else lock2
            if lock1
     */
    @Override
    public void run() {
        if(flag){
            //d1
            synchronized (MyLock.lock1){
                System.out.println("if lock1");
                //d1
                synchronized (MyLock.lock2){
                    System.out.println("if lock2");
                }
            }
        }else {
            //d2
            synchronized (MyLock.lock2){
                System.out.println("else lock2");
                //d2
                synchronized (MyLock.lock1){
                    System.out.println("else lock1");
                }
            }
        }

    }
}

創建實作類DieLockDemo:

public class DieLockDemo {
    public static void main(String[] args) {
        DieLock d1 = new DieLock(true);
        DieLock d2 = new DieLock(false);

        d1.start();
        d2.start();
    }
}

運行結果:

if lock1
else lock2

else lock2
if lock1

在我們今后的開發中要避免鎖的同步嵌套使用,不然就很容易產生死鎖問題


執行緒組

查看API檔案我們知道:

public Thread(ThreadGroup group,Runnable target,String name)

分配一個新的Thread物件,使其具有target作為其運行物件,具有指定的name作為其名稱,屬于group參考的執行緒組,

如果有安全管理器,則使用ThreadGroup作為引數呼叫其checkAccess方法,

此外,它的checkPermission方法由RuntimePermission("enableContextClassLoaderOverride")權限呼叫,直接或間接地由覆寫getContextClassLoadersetContextClassLoader方法的子類的getContextClassLoader setContextClassLoader呼叫,

新創建的執行緒的優先級設定為等于創建執行緒的優先級,即當前正在運行的執行緒, 可以使用方法setPriority將優先級改變為新值,

當且僅當創建它的執行緒當前被標記為守護執行緒時,新創建的執行緒才被初始化為守護執行緒, 方法setDaemon可以用于改變執行緒是否是守護行程,

引數

group - 執行緒組, 如果是null并且有一個安全管理器,則該組由SecurityManager.getThreadGroup()決定 , 如果沒有安全管理員或SecurityManager.getThreadGroup()回傳null ,該組將設定為當前執行緒的執行緒組,

target - 啟動此執行緒時呼叫其run方法的物件, 如果null ,則呼叫此執行緒的run方法,

name - 新執行緒的名稱

例外

SecurityException - 如果當前執行緒不能在指定的執行緒組中創建執行緒,或者不能覆寫背景關系類加載器方法,

Java中使用ThreadGroup來表示執行緒組,它可以對一批執行緒進行分類管理, Java允許程式直接對執行緒組進行控制,


參考代碼:

創建MyRunnable類:

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

創建實作類ThreadGroupDemo:

public class ThreadGroupDemo {
    public static void main(String[] args) {
        //創建自定義類物件
        MyRunnable myRunnable = new MyRunnable();

        //創建執行緒物件
        Thread t1 = new Thread(myRunnable, "劉德華");
        Thread t2 = new Thread(myRunnable, "張學友");

        //我們之前都沒有提過執行緒組,以及分組的概念,想著應該有一個默認的分組
        //獲取分組
        //ThreadGroup getThreadGroup()
        //回傳此執行緒所屬的執行緒組,
        ThreadGroup tg1 = t1.getThreadGroup();
        System.out.println(tg1);
        ThreadGroup tg2 = t2.getThreadGroup();
        System.out.println(tg2);

        //獲取執行緒組的名字
        //String getName()
        //回傳此執行緒組的名稱,
        System.out.println(tg1.getName());
        System.out.println(tg2.getName());
        System.out.println(Thread.currentThread().getName());


        //需求:給執行緒分組
        //Thread(ThreadGroup group, Runnable target, String name)
        //分配一個新的 Thread物件,使其具有 target作為其運行物件,
        // 具有指定的 name作為其名稱,屬于 group參考的執行緒組,
        //創建一個新的執行緒組
        //ThreadGroup(String name)
        //構造一個新的執行緒組,
        ThreadGroup tg3 = new ThreadGroup("下路三人組");

        //創建執行緒物件
        Thread t3 = new Thread(tg3, myRunnable, "榮耀行刑官");
        Thread t4 = new Thread(tg3, myRunnable, "魂鎖典獄長");
        Thread t5 = new Thread(tg3, myRunnable, "蒸汽機器人");

        System.out.println(t3.getThreadGroup().getName());
        System.out.println(t4.getThreadGroup().getName());

        //Java允許程式直接對執行緒組進行控制
        //直接通過組名設定這一組都是守護執行緒,組里面的執行緒都是守護執行緒
        tg3.setDaemon(true);


    }
}

運行結果:

java.lang.ThreadGroup[name=main,maxpri=10]
java.lang.ThreadGroup[name=main,maxpri=10]
main
main
main
下路三人組
下路三人組


執行緒池Executors

newFixedThreadPool

查看API檔案我們知道:

public static ExecutorService newFixedThreadPool(int nThreads)

創建一個執行緒池,該執行緒池重用固定數量的從共享無界佇列中運行的執行緒, 在任何時候,最多nThreads執行緒將處于主動處理任務, 如果所有執行緒處于活動狀態時都會提交其他任務,則它們將等待佇列中直到執行緒可用, 如果任何執行緒由于在關閉之前的執行期間發生故障而終止,則如果需要執行后續任務,則新執行緒將占用它, 池中的執行緒將存在,直到它明確地為shutdown

引數

nThreads - 池中的執行緒數

結果

新創建的執行緒池

例外

IllegalArgumentException - 如果是 nThreads <= 0

程式啟動一個新執行緒成本是比較高的,因為它涉及到要與作業系統進行互動,

而使用執行緒池可以很好的提高性能,尤其是當程式中要創建大量生存期很短的執行緒時,
更應該考慮使用執行緒池,
執行緒池里的每一個執行緒代碼結束后,并不會死亡,而是再次回到執行緒池中成為空閑狀態,
等待下一個物件來使用,
在JDK5之前,我們必須手動實作自己的執行緒池,從JDK5開始,Java內置支持執行緒池

如何實作執行緒池的代碼呢?
    1、創建執行緒池物件,Executors工廠類下的靜態方法
        newFixedThreadPool是其中一種執行緒池
        public static ExecutorService newFixedThreadPool(int nThreads)
    2、如何往執行緒池中存放執行緒?(可以存放哪些執行緒呢?)
    3、在執行緒池中的執行緒如何運行呢?
    4、我想結束執行緒的運行,可不可以手動結束呢?如果可以,怎么結束?

參考代碼1:

public class ThreadPoolDemo {
    public static void main(String[] args) {
        //創建執行緒池
        ExecutorService pool = Executors.newFixedThreadPool(2);

        //Future<?> submit(Runnable task)
        //提交一個可運行的任務執行,并回傳一個表示該任務的未來,
        MyRunnable myRunnable = new MyRunnable();
        pool.submit(myRunnable);
        pool.submit(myRunnable);
        //提交數超過執行緒池的數量的時候也會執行,只不過是當有空閑執行緒位置的時候再去執行
        //newFixedThreadPool最大一次性可執行執行緒數量為初始設定的數量
        pool.submit(myRunnable);

        //我想結束執行緒的運行,可不可以手動結束呢?如果可以,怎么結束?
        //void shutdown()
        //啟動有序關閉,其中先前提交的任務將被執行,但不會接受任何新任務,
        pool.shutdown();

        System.out.println("======================================");
        //RejectedExecutionException
        //執行緒池已經被關閉了,不能再提交任務執行
        pool.submit(myRunnable);

    }
}

運行結果:

提交數超過執行緒池的數量的時候也會執行,只不過是當有空閑執行緒位置的時候再去執行newFixedThreadPool最大一次性可執行執行緒數量為初始設定的數量

創建執行緒的第三種方式

callable

查看API檔案我們知道

public static Callable<Object> callable(PrivilegedExceptionAction<?> action)

回傳一個Callable物件,該物件在被呼叫時運行給定的特權例外操作并回傳其結果,

引數

action - 運行的特權例外操作

結果

可呼叫物件

例外

NullPointerException - 如果動作為空

參考代碼2:
創建自定義類MyCallable:

import java.util.concurrent.Callable;

public class MyCallable implements Callable {
    @Override
    public Object call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
        return null;
    }
}

創建實作類ThreadPoolDemo2:

public class ThreadPoolDemo2 {
    public static void main(String[] args) {
        //創建執行緒池物件
        ExecutorService pool = Executors.newFixedThreadPool(2);

        MyRunnable myRunnable = new MyRunnable();
        MyCallable myCallable = new MyCallable();

        //可以執行Runnable物件或者Callable物件代表的執行緒池
        pool.submit(myRunnable);
        pool.submit(myCallable);

        pool.shutdown();


    }
}

運行結果:


匿名內部類方式使用多執行緒

參考代碼:

public class ThreadDemo2 {
    public static void main(String[] args) {
        Thread t1 = new Thread("靚仔"){
            @Override
            public void run() {
                for (int i = 0;i < 100;i++){
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
        };
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0;i < 100;i++){
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
        },"劉德華");
        t2.start();
    }
}

運行結果:


定時器

定時器是一個應用十分廣泛的執行緒工具,可用于調度多個定時任務以后臺執行緒的方式執行,在Java中,可以通過Timer和TimerTask類來實作定義調度的功能,

Timer

查看API檔案我們知道:

public class Timer extends Object

執行緒調度任務以供將來在后臺執行緒中執行的功能, 任務可以安排一次執行,或定期重復執行,

對應于每個Timer物件是單個后臺執行緒,用于依次執行所有定時器的所有任務, 計時器任務應該快速完成, 如果一個定時器任務需要花費很多時間來完成,它會“計時”計時器的任務執行執行緒, 這可能會延遲隨后的任務的執行,這些任務在(和)如果違規任務最后完成時,可能會“束起來”并快速執行,

在最后一次對Timer物件的參考后所有未完成的任務已完成執行,定時器的任務執行執行緒正常終止(并被收集到垃圾回收), 但是,這可能需要任意長時間的發生, 默認情況下,任務執行執行緒不作為守護程式執行緒運行,因此它能夠使應用程式終止, 如果主叫方想要快速終止定時器的任務執行執行緒,則呼叫者應該呼叫定時器的cancel方法,

如果定時器的任務執行執行緒意外終止,例如,因為它呼叫了stop方法,那么在計時器上安排任務的任何進一步的嘗試將會產生一個IllegalStateException ,就像定時器的cancel方法被呼叫一樣,

這個類是執行緒安全的:多個執行緒可以共享一個單獨的Timer物件,而不需要外部同步,

此類提供實時保證:使用Object.wait(long)方法是調度任務,

Java 5.0引入了java.util.concurrent軟體包,其中一個java.util.concurrent程式是ScheduledThreadPoolExecutor ,它是用于以給定速率或延遲重復執行任務的執行緒池, 這實際上是對一個更靈活的替代Timer / TimerTask組合,因為它允許多個服務執行緒,接受各種時間單位,并且不需要子類TimerTask (只實作Runnable ), 使用一個執行緒配置ScheduledThreadPoolExecutor使其等同于Timer

實作注意事項:這個類可以擴展到大量并發計劃任務(千應該沒有問題), 在內部,它使用二進制堆表示其任務佇列,因此計劃任務的成本為O(log n),其中n為并發計劃任務的數量,

實作注意事項:所有建構式啟動計時器執行緒,

參考代碼1:

public class TimerDemo {
    public static void main(String[] args) {
        //創建定時器物件
        //Timer()
        //創建一個新的計時器,
        Timer timer = new Timer();

        //調度任務執行
        //在指定的延遲之后安排指定的任務執行, 定時在未來的某一時刻執行任務
        //public void schedule(TimerTask task, long delay)
        //自定義子類的形式創建任務
        //delay這個型別是毫秒
        timer.schedule(new MyTask(timer), 3000);
        //public void cancel()終止此計時器,丟棄任何當前計劃的任務
        //timer.cancel();
//        timer.schedule(new MyTask(timer), 3000);
    }
}

class MyTask extends TimerTask{
    private Timer timer;

    public MyTask(Timer timer){
        this.timer = timer;
    }


    @Override
    public void run() {
        System.out.println("靚仔真帥");
        timer.cancel();
    }
}

運行結果:

靚仔真帥

參考代碼2:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

public class TimerDemo2 {
    public static void main(String[] args) {
        //創建定時器物件
        Timer timer = new Timer();

        //void schedule(TimerTask task, long delay, long period)
        //在指定的延遲之后開始 ,重新執行 固定延遲執行的指定任務,
        //3秒后執行任務,并且每個2秒執行一次任務
        timer.schedule(new MyTask2(),3000,2000);
    }
}

class MyTask2 extends TimerTask{

    @Override
    public void run() {
        try {
            FileReader fr = new FileReader("src\\liangzai.txt");
            BufferedReader br = new BufferedReader(fr);
            System.out.println(br.readLine());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

運行結果:

通過觀察運行結果發現,3秒后輸出靚仔真帥!,之后每兩秒輸出一次靚仔真帥!


到底啦!給靚仔一個關注吧!? つ ?_? ?つ

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/398606.html

標籤:其他

上一篇:Windows權限維持1:賬號隱藏

下一篇:Python串列常用函式使用詳解(內附詳細案例)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more