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")
浙公网安备 33010602011771号