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("主執行緒執行完畢"); }
第一: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")