1. 程式人生 > 其它 >TwentyFourDay-java wait與notify,獲取位元組碼

TwentyFourDay-java wait與notify,獲取位元組碼

1、執行緒分為兩大類

使用者執行緒與守護執行緒(後臺執行緒)

守護執行緒:

代表性的守護執行緒:垃圾回收機制。

特點:一般是個死迴圈,所有的使用者執行緒只要結束。守護執行緒自動結束。

t1.setDaemon(true);//把分支執行緒設定為守護執行緒,使用者執行緒結束,守護執行緒也結束

2、定時器

間隔特定的時間,執行特定的程式

利用sleep方法,是最原始的定時器

Java已經寫好了一個定時器:java.util.Timer。

在實際開發中,使用較多的是Spring框架中提供的SpringTask框架。

//TimesThread執行緒

//TimerTask實現了Runnable介面,也是一個執行緒機制
class TimesThread extends TimerTask{
​
    @Override
    public void run() {
        Date date=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        String d=sdf.format(date);
        System.out.println(d+"完成了一次備份!");
    }
}

//定時器

public static void main(String[] args) throws Exception{
    //建立一個定時器物件
    Timer time=new Timer();
    SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date=sdf.parse("2021-07-17 09:50:30");
    //time.schedule(執行緒物件,程式起始時間,每個多長時間執行一次)
    time.schedule(new TimesThread(),date,1000*10);
}

3、執行緒的實現方式第三種

實現執行緒的第三種方式:實現Callable介面 優點:可以獲取執行緒的執行結果 缺點:效率較低

public static void main(String[] args) {
        //建立一個“未來任務類”物件,引數是Callable介面實現類物件(可以收到返回值)
        //引數是Runnable介面實現類物件(接收不到返回值)
        FutureTask futureTask=new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                System.out.println("begin");
                Thread.sleep(1000*5);//當前執行緒睡眠五秒
                System.out.println("end");
                int a=10;
                int b=10;
                return a+b;
            }
        });
        //把未來任務類物件封裝為執行緒
        Thread thread=new Thread(futureTask);
        thread.start();
​
        //這行程式碼會阻塞主執行緒的執行,只有當支執行緒執行完畢,獲取到值後才可以繼續執行主執行緒
        try {
            //取返回值
            Object o = futureTask.get();
            System.out.println(o);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //當get獲取到分支執行緒的結果後才可以執行這段程式碼,時間可能很長
        System.out.println("主執行緒執行完畢");
    }

4、關於Object類中的wait與notify方法 (生產者和消費者模式)

第一:wait和notify方法不是執行緒物件的方法,是java中任何一個java物件的方法

wait方法和notify方法不是通過執行緒物件呼叫的。

第二:wait方法的作用?

Object o=new Object();

o.wait(); 表示在O上的活動執行緒進入無期限等待,直到被喚醒為止

第三:notify方法的作用?

o.notify();喚醒在o上等待的執行緒

notifyAll():喚醒所有在o上等待的執行緒

第四:wait與notify方法建立線上程同步的基礎之上

小練習:

兩個執行緒交替輸出奇數和偶數如:t1-->1 t2-->2 t3-->3 t4-->4......

//建立一個數字類,用來累加

class Number {
    private int i;
    public Number(int i){
        this.i=i;
    }
​
    public int getI() {
        return i;
    }
​
    public void setI(int i) {
        this.i = i;
    }
​
    public void insertnum(int i){
        ++i;
        this.setI(i);
    }
}

//執行緒1

class OneThread extends Thread{
    private Number num;
    public OneThread(Number num){
        this.num=num;
    }
    public void run(){
        //把共享物件num鎖住
        synchronized (num) {
            while (true) {
                //判斷是不是偶數,要是偶數執行緒進入無限期等待
                if (num.getI() % 2 == 0) {
                    try {
                        num.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //程式走到這裡說明,i是奇數,先睡眠兩秒
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //輸出
                System.out.println(Thread.currentThread().getName() + "-->" + num.getI());
                num.insertnum(num.getI());
                //喚醒執行緒2
                num.notify();
            }
        }
    }
}

//執行緒2

class TwoThread extends Thread{
    private Number num;
    public TwoThread(Number num){
        this.num=num;
    }
    public void run(){
        synchronized (num) {
            while (true) {
                //判斷是否是奇數。若是執行緒進入無限期等待
                if (num.getI() % 2 != 0) {
                    try {
                        num.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //走到這裡說明i為偶數,先睡2s
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //輸出
                System.out.println(Thread.currentThread().getName() + "-->" + num.getI());
                //呼叫累加方法
                num.insertnum(num.getI());
                //喚醒執行緒1
                num.notify();
            }
        }
    }
}

//主執行緒

public static void main(String[] args) {
    //Number物件
    Number n=new Number(0);
    //兩個執行緒共享一個物件
    Thread t1=new OneThread(n);
    Thread t2=new TwoThread(n);
    t1.setName("t1");
    t2.setName("t2");
    t1.start();
    t2.start();
​
}

5、反射機制

1>反射機制有什麼用?

通過java語言中的反射機制可以操作位元組碼檔案

2>反射機制的相關類在哪個包下?

java.lang.reflect.*;

3>反射機制相關的重要的類有哪些?

java.lang.Class:代表整個位元組碼

java.lang.reflect.Method:代表位元組碼中的方法位元組碼

java.lang.reflect.Constructor:代表位元組碼中的構造方法

java.lang.reflect.Field:代表位元組碼中的屬性位元組碼

6、獲取位元組碼的三種方式

  • 第一種方式 Class.forName("完整類名帶包名");
  • 第二種方式 Class c1=物件.getClass();

  • 第三種方式 Class c2=型別.class;

public static void main(String[] args) {
    //第一種方式 Class.forName("完整類名帶包名");
    Class c1=null;
    try {
         c1=Class.forName("java.lang.String");//c1代表String.class檔案
        Class c2=Class.forName("java.util.Date");//c2代表Date.class檔案
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    //java語言中任何一個物件都有一個方法:getClass()
    String s="ads";
    Class x=s.getClass();//x代表String.Class位元組碼檔案,x代表String型別
    System.out.println(x==c1);//true 說明x與c1指向的是同一份String位元組碼檔案
    //第三種方式:java語言中任何一種型別都有.class屬性
    Class y=int.class;
    Class z=String.class;
    System.out.println(z==x);//true
}

7、關於Class的forName方法

//再此回憶一下靜態程式碼塊

class MyClass {
    //靜態程式碼塊,在類載入的時候執行,只執行一次
    static {
        System.out.println("靜態程式碼塊執行了!");
    }
}

//forName

public static void main(String[] args) {
   try {
        //當只需要執行一個類的靜態程式碼塊,其他程式碼不需要執行,則就可以採用如下方式
        //Class.forName(“完整類名”);這個方法執行會導致類的載入,類載入靜態程式碼塊執行
        Class c1=Class.forName("TWentyFourDay.MyClass");
        System.out.println(c1);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

8、以反射機制建立物件

newInstance()//new一個新物件 已過時

public static void main(String[] args) {
    //不以反射機制建立物件 這種是寫死的
    User u1=new User();
    System.out.println(u1);//TWentyFourDay.User@6e8dacdf
    //通過獲取位元組碼檔案(反射機制)建立物件,這種方式更加靈活
    try {
        Class c1= Class.forName("TWentyFourDay.User");
        //重點:newInstance會呼叫無參構造方法去完成物件的建立。無參構造必須存在!
        //如果User只有有參構造,沒有無參構造,則會在newInstance建立物件的時候出現InstantiationException例項化異常
        Object o = c1.newInstance();
        System.out.println(o);//TWentyFourDay.User@6e8dacdf
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

9、驗證反射機制的靈活性

通過屬性文件讀取資料進行建立物件 驗證反射機制的靈活性

java程式碼不用改變,就可以建立不同物件,修改配置檔案即可

public static void main(String[] args) {
    try {
        //建立流
        FileInputStream file=new FileInputStream("java基礎語法/text.properties");
        //map集合
        Properties list=new Properties();
        //載入
        list.load(file);
        //流關閉
        file.close();
        //從屬性檔案中以鍵的形式獲取值(在檔案中有“username=TWentyFourDay.User”)
        String path=list.getProperty("username");
        Class c=Class.forName(path);
        Object o = c.newInstance();//這裡相當於是 User o=new User();
        //如果檔案中是“username=java.util.Date”,則相當於這裡是 Date o=new Date();
        System.out.println(o);//TWentyFourDay.User@7a79be86 (Sat Jul 17 19:14:13 CST 2021)
​
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

10、通用的路寫法

一種比較通用的路徑寫法,即使跨作業系統,程式碼位置改變,依舊可以。 但是這種方式的前提是,檔案必須在類路徑下,類路徑指的是在src下 src是類的根路徑(不準確,可以這樣理解)

String path=Thread.currentThread().getContextClassLoader().getResource("tempfile03").getPath();
System.out.println(path);//返回的是檔案的絕對路徑

對路徑程式碼的解釋:

Thread.currentThread():當前執行緒

getContextClassLoader():執行緒物件的方法,可以獲得當前執行緒的類載入器物件

getResource(""):這是類載入器物件的方法,當前執行緒的類載入器預設從類的根路徑下載入資源

Thread.currentThread().getContextClassLoader().getResource("tempfile03")