0%

多线程、注解和反射

多线程

概念

线程分为执行时间与等待事件,在一个线程的等待事件中可以执行其他线程

img

img

img

线程继承于线程类Thread并重写run方法,当线程启动时自动执行run方法

多线程的运行顺序时随机的

线程分为用户线程与守护线程,守护线程为其他线程提供服务,如垃圾回收器(GC),且不能单独运行,当JVM中没有其他用户线程只有守护线程时,守护线程自动销毁,JVM会退出。

创建线程

  1. 声明类继承Thread类并创建一个该类对象

    1
    MyThread thread=new MyThread();
  2. 声明类实现Runnable接口并创建一个该类对象,声明一个Thread类对象(传入Runnable对象)

    1
    2
    MyRunnable runnable=new MyRunnable();
    Thread thread=new Thread(runnable);
  3. 声明类实现Callable接口并创建一个该类对象,创建一个FutureTask对象并调用

    1
    2
    3
    Callable<Integer> callable = new MyCallable();
    FutureTask<Integer> futureTask = new FutureTask<>(callable);
    Thread thread = new Thread(futureTask);

线程的一些常用方法

1
2
3
4
5
6
7
8
currentThread()                当前运行的线程
isAlive() 获取线程是否运行
sleep(num) 让线程休眠一段时间
getid() 获取线程ID
yield() 线程让步,放弃CPU资源
setPriority(num) 设置线程优先级(1~10)
interrupt() 线程中断标志
setDaemon() 设置为守护线程

附:

  1. 当前线程表示运行该代码时运行的代码所在线程,如在main线程中使用MyThread类构造方法时处于main线程中
  2. 某线程结束运行后其ID可能被后续线程使用
  3. 优先级越高的线程获得的CPU资源越多,不能保证优先级越高的线程先运行,优先级设置不当可能会导致某些线程永远无法得到运行,即线程饥饿,一般编写代码不进行线程优先级设置
  4. 线程标志中断后该线程需要自己检测是否中断并执行
  5. 设置守护线程应该在线程运行之前就设置完毕

线程生命周期

线程生命周期可以通过getState()方法获得,线程状态有:

  • NEW,新建状态,即start()启动之前
  • RUNNABLE,可运行状态,是一个复合状态,包括READY和RUNNING,READY状态可以被线程调度器调度使之处于RUNNING状态,RUNNING状态可通过Thread.yield()方法转换为READY状态
  • BLOCKED,阻塞状态,线程发起阻塞的I/O操作,或申请由其他线程的独占资源,线程会转换为BLOCKED,不会占用CPU资源,当发起阻塞的I/O操作结束或获得申请的资源,线程可以转为RUNNABLE
  • WAITING,等待状态,线程执行了Object.wait(),Thread.join()方法转换为WAITING状态,执行Object.notify()方法,或加入线程完毕转换为RUNNABLE状态
  • TIMED_WAITING,类似于WAITING,但线程如果没有在指定时间范围完成期望操作,该线程会自动转换为RUNNABLE
  • TERMINATED,终止状态,线程结束

其他性质

线程原子性

线程对数据操作的不可分割性

使一个数同一时间只能被一个线程调用定义AtomicInteger变量

线程可见性

一个线程对数据的更新结果可以被其他线程读取则称为可见

重排序

不同处理器上执行的的多个操作顺序可能不同,可能导致线程安全问题

注解

@+注释名在代码中存在,如重写注解@Override

内置注解

注解名 含义
@Override 重写
@Deprecated 已过时,不推荐使用的方法
@SuppressWarnings() 镇压警告

元注解

负责注解其他注解的注解

注解名 含义
@Target() 描述注解使用范围
@Retention() 在什么级别保存注释信息(SOURCE,CLASS,RUNTIME)
@Documented 说明注解将生成在javadoc中
@Inherited 说明子类可以继承父类中的该注解

自定义注解

使用@interface自定义注解

其中声明的参数在使用时必须添加,可以有默认值

1
2
3
4
@interface MyAnnotation{
String name() default "L";
int age();
}

反射

区分静态动态语言

image-20201227162131463

image-20201227162540384

Class类

image-20201227165419393

常用方法

方法名 功能
static Class.forName(String name) 返回指定名的Class对象
Object newInstance() 调用缺省构造函数,返回Class对象实例
getName() 返回此Class对象表示的实体名
Class getSuperClass() 返回当前Class对象的父类Class对象
Class[] getinterfaces() 获取当前对象接口
ClassLoader getClassLoader() 返回该类的类加载器
ClassLoader getConstructors() 返回包含某些Constructors对象的数组
Method getMethod(String name, Class...T) 返回Method对象,此对象形参类型为paramType
Field[] getDeclaredFields() 返回Field对象的数组

有Class对象的类型

image-20201227202420407

注意:一个类/类型只有一个Class对象

如:类型相同长度不同的两个数组是同一对象,两者getClass()的hashCode相同

image-20201227204625890

主动/被动初始化

image-20201227205857255

类加载器

image-20201227211950728

image-20201227212032776

双亲委派机制

如果一个类加载器收到了一个类加载请求,它不会自己去尝试加载这个类,而是把这个请求转交给父类加载器去完成。每一个层次的类加载器都是如此。因此所有的类加载请求都应该传递到最顶层的启动类加载器中,只有到父类加载器反馈自己无法完成这个加载请求(在它的搜索范围没有找到这个类)时,子类加载器才会尝试自己去加载。委派的好处就是避免有些类被重复加载。

双亲委派机制

获取类运行时的结构

1
2
3
4
5
6
7
8
9
10
Class c1 = Class.forName("")//获取Class对象

c1.getName()获取类名
c1.getSimpleName()获取简单名
c1.getFields()获取public属性,添加参数可获得特定属性
c1.getDeclaredFields()获取全部属性
c1.getMethods()获取本类及父类全部public方法,添加参数可获得特定方法
c1.getDeclaredMethods()获取本类全部方法
c1.getConstructor()获取public构造方法,添加参数可获得特定方法
c1.getDeclaredConstructor()获取全部构造方法

得到Class对象之后

image-20201227214124782

通过构造器实例化对象

1
2
3
4
Constructor constructor;
constructor = Class.forName("User").getDeclaredConstructor(String.class,int.class,int.class);
User user1 = (User) constructor.newInstance("张三", 18, 108);
System.out.println(user1);

注:constructor.newInstance()实例化新对象

通过反射调用普通方法

1
2
3
4
User user1 = new User();
Method setName = Class.forName("User").getDeclaredMethod("setName", String.class);
setName.invoke(user1,"张三");
System.out.println(user1.getName());

通过反射更改私有属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
User user1 = new User();
Field name = Class.forName("User").getDeclaredField("name");
name.setAccessible(true);//设置关闭安全检测
name.set(user1,"李四");
System.out.println(user1.getName());

注:`Class.setAccessible`是获取到的各种对象的通用方法,可以提高反射效率

#### 反射获取泛型

![image-20201227222104618](https://i.loli.net/2020/12/27/A5cMGmvV6FCew94.png)

#### 反射获取注解

![image-20201227222904779](https://i.loli.net/2020/12/27/joiJBuqxedZzaOS.png)