ascetic

monk


  • 首页

  • 标签

  • 分类

  • 归档

类加载机制

发表于 2019-03-01 | 分类于 JVM

类加载过程

加载

查找并加载类的二进制数据。在该阶段虚拟机需要完成3件事情:

1.​通过一个类的全限定名来获取类定义此类的二进制流。

2.​将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

3.​在内存(堆区)中生成一个代表这个类的java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。

连接

连接是将已经读入内存的类的二进制数据合并到虚拟机的运行时环境中去。

验证

​确保被加载的类的正确性。验证过程主要分为4个阶段分别为:文件格式校验、元数据校验,字节码校验、符号引用验证。

准备

​为类的静态变量分配内存,并将赋予默认值,例如:该阶段value的初始值为0,该阶段为value赋值为0,而不是123。

1
public static int value=123;

解析

​解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。该阶段可能发生在初始化之前也可能发生在初始化阶段之后。

初始化

​为类的静态变量赋予正确的初始值。如上例子中:该阶段才完成value赋值为123。

类初始化时机有且只有以下五种情况:

1.遇到new (新建对象时),getstatic(读取静态变量时),putstatic(写一个的静态变量)或invokestatic(调用静态方法时)这4条字节码指令时。

2.使用java.lang.reflect 包的方法对类进行反射调用的时候。

3.当初始化一个类的时候,如果发现父类还没有初始化,则先需要先触发器父类的初始化。

4.当虚拟机启动时,用户需要制定一个执行的主类(main 方法所在类),虚拟机会先初始化这个类。

5、动态语言支持中,如果一个java.lang.invoke.MethodHandler实例返回的戒指结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

类加载器的分类

从Java虚拟机的角度来讲,只存在两种不同的类加载器: 一种是启动类加载器,这个类加载器使用C++语言实现,是虚拟机的一部分;另一种就是所有的其他的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并且全部都技能自抽象类java.lang,ClassLoader。

java虚拟机自带的加载器

启动类加载器(Bootstrap)

这个加载器负责将<JAVA_HOMT>\lib 目录中的,或者被-Xbootclasspath参数所指定的路径中的并且是虚拟机识别的类库加载到虚拟机内存中。程序员无法再java代码中获取该类。

扩展类加载器(Extension)

j这个加载器由sun.misc.Launcher$ExtClassLoader实现,负责加载<JAVA_HOME>\lib\ext 目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器

应用加载器(System)

这个加载器有sun.misc.Launcher$App-ClassLoader实现。由于该类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也成为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库。所以开发者可以直接使用这个类加载器,如果应用程序中没有自定义自己的类加载器,一般情况下这个就是程序中默认的类加载器。

用户自定义的类加载器

用户自定义类加载器,必须是java.lang.ClassLoader的子类,可以定义类的加载行为。

双亲委派模型

双亲委派模型要求除了顶层的启动类加载外,其余的类加载器都应当有自己的父类加载器,这里类加载器的父子关系一般不会以继承(Inheritance)的关系来实现,而是都使用组合(Cpmposition)关系来复用附加在其的代码 。

双亲委派模型的工作过程

如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个请求时,子加载器才会去尝试自己去加载。双亲委派模型的好处是:能够有效确保一个类的全局唯一性。

多线程-基本概念

发表于 2019-01-25 | 分类于 多线程

多线程

重入

当某个线程请求一个由其他线程获取持有的锁时,该线程就会阻塞。然而,由于synchronized是可重入的,因此如果某个锁线程获取一个已经由它自己持有的锁时,这个请求就会成功。重入意味着获取锁的粒度是线程而不是调用。

重入的一种方法是,给锁关联一个计数器和一个持有者线程。当计数器为0时说明该线程未被线程持有。当线程请求一个未持有的锁时,JVM将记下锁的持有者并且将计数值设置为1,如果同一线程再次访问时将计数器加1,当同一线程退出同步代码块时,计数器减1,当技术器为0时,该锁将被释放。

volatile

当变量被声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

synchronized实现原理

synchronized 同步语句块使用的是monitorenter和monitorexit指令,其中monitorenter指令指向同步代码块的开始位置,monitorexit指令则指明同步代码块的结束位置。
synchronized 修饰方法使用的是ACC_SYNCHRONIZED 标识

ReentrantLock实现原理

ReentrantLock 通过AbstractQueuedSynchronizer(即AQS)实现.用FIFO队列实现公平锁和非公平锁,在公平锁中,如果当另一个线程持有锁或者有其他线程在等待队列中这个锁,所以新发出的请求的线程将被放入到度一列中。而非公平锁,只有当锁被其他线程持有时,新发出请求的线程才会被放入队列中(此时和公平锁是一样的)。所以他们的差别是非公平锁会有更多的机会去抢占锁。

ReenTrantLock的对比

1、两者都是可重入锁

2、synchronized依赖于JVM而ReenTrantLock依赖于API

3、ReenTrantLock比synchronized增加了一些高级功能主要有:等待可中断、可实现公平锁、可实现选择性通知

  1. ReenTrantLock提供了一种能够中断等待锁的线程机制,通过lock.lockInterruptibly()来实现这个机制。
  2. ReenTrantLock 可以指定公平锁和非公平锁,而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。ReenTrantLock默认情况是公平的。
  3. synchronized关键字与wait()和notify/notifyAll()方法相结合可以实现等待/通知机制,ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition() 方法。Condition是JDK1.5之后才有的,它具有很好的灵活性,比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。 在使用notify/notifyAll()方法进行通知时,被通知的线程是由 JVM 选择的,用ReentrantLock类结合Condition实例可以实现“选择性通知”。

AQS实现原理

AQS核心思想是,如果请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源占用,那么需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制是AQS是用CLH(FIFO双向队列)队列锁实现的,即将暂时获取不到的线程加入到队列中。

AQS定义两种资源共享方式

Exclusive(独占);只有一个线程能执行如ReentrantLock.又可分为公平锁和非公平锁:

  1. 按照线程在队列中的排队顺序,先到者先拿到锁
  2. 当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的
    Share(共享):多个线程可同时执行,如Semaphore/CountDownLatch。Semaphore、CountDownLatch、 CyclicBarrier、ReadWriteLock。

AQS使用了模板方法模式,自定义同步器时需要重写下面几个AQS提供的模板方法:

1
2
3
4
5
isHeldExclusively()//该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int)//独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int)//独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int)//共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true,失败则返回false。

自定义泛型TypeHandler的使用

发表于 2019-01-24 | 分类于 mybatis

泛型TypeHandler的使用

在上一篇博客中讲述了,TypeHandler的基本使用。但是对于某些结构对应多种类型。例如数据库字段为json结构,映射对象字段为自定义类型,那么难道到每种类型都要分别做处理吗?接下里稍作改造typeHandler,就可以完美适配。

1、将TypeHandler改为泛型的TypeHandler

1
JsonbTypeHandler<T> extends BaseTypeHandler<T>

2、给该类添加参数type并添加构造函数

1
2
3
4
5
6
7

private Class<T> type;

public JsonbTypeHandler(Class<T> type) {
if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
this.type = type;
}

3、添加映射类型

每次添加需要映射的类型,只要在@MappedTypes注解中添加需要映射的类型就可以了

完整的带泛型的TypeHandler代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
@MappedJdbcTypes(value = {JdbcType.OTHER}, includeNullJdbcType = true)
@MappedTypes({ A.class , B[].class})
public class JsonbTypeHandler<T> extends BaseTypeHandler<T> {

private Logger logger = LoggerFactory.getLogger(getClass());

private ObjectMapper objectMapper = new ObjectMapper();

private Class<T> type;

public JsonbTypeHandler(Class<T> type) {
if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
this.type = type;
}

/**
* 设置非空参数
*
* @param ps PreparedStatement
* @param parameterIndex 参数
* @param parameter 参数数组
* @param jdbcType jdbcType
* @throws SQLException
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int parameterIndex, T parameter, JdbcType jdbcType)
throws SQLException {
String jsonText;
try {
jsonText = objectMapper.writeValueAsString(parameter);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}

ps.setString(parameterIndex, jsonText);
}

/**
* 获取可以为null的结果集
*
* @param resultSet 结果集
* @param columnName 列名
*/
@Override
public T getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
String jsonText = resultSet.getString(columnName);
return readToObject(jsonText);
}

/**
* 获取可以为null的结果集
*
* @param resultSet 结果集
* @param columnIndex columnIndex 列标
*/
@Override
public T getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
String jsonText = resultSet.getString(columnIndex);
return readToObject(jsonText);
}

/**
* 获取可以为null的结果集
*
* @param callableStatement CallableStatement
* @param columnIndex 列号
*/
@Override
public T getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
String jsonText = callableStatement.getString(columnIndex);
return readToObject(jsonText);
}

private T readToObject(String jsonText) {
if (StringUtils.isEmpty(jsonText)) {
return null;
}

try {
T list = objectMapper.readValue(jsonText, type);
return list;
} catch (IOException e) {
logger.error("字符串反序列化失败字符串为{}",jsonText,e);
}

return null;
}
}

自定义typeHandler的使用

发表于 2019-01-23 | 分类于 mybatis

简单TypeHandler 的使用

mybatis是一个常用的一个ORM框架,但是mybatis提供的TypeHandler往往是不够用的,例如某些数据库支持json、数组等的支持,但是mybatis对于这些数据类型没有支持,接下来就需要我们自定义TypeHandler了。

1、创建TypeHandler类继承BaseTypeHandler。

下面是一个针对数组类型的TypeHandler,并实现方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
public class ArrayTypeHandler extends BaseTypeHandler<Object[]> {
private static final String TYPE_NAME_VARCHAR = "varchar";
private static final String TYPE_NAME_INTEGER = "integer";
private static final String TYPE_NAME_BOOLEAN = "boolean";
private static final String TYPE_NAME_NUMERIC = "numeric";

private Logger logger = LoggerFactory.getLogger(this.getClass());

/**
* 设置非空参数
*
* @param ps PreparedStatement
* @param parameterIndex 参数i
* @param parameter 参数数组
* @param jdbcType jdbcType
* @throws SQLException
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int parameterIndex, Object[] parameter, JdbcType jdbcType) throws SQLException {
String typeName = null;
if (parameter instanceof Integer[]) {
typeName = TYPE_NAME_INTEGER;
} else if (parameter instanceof String[]) {
typeName = TYPE_NAME_VARCHAR;
} else if (parameter instanceof Boolean[]) {
typeName = TYPE_NAME_BOOLEAN;
} else if (parameter instanceof Double[]) {
typeName = TYPE_NAME_NUMERIC;
}

if (typeName == null) {
throw new TypeException("ArrayTypeHandler parameter typeName error, your type is " + parameter.getClass().getName());
}

// 这3行是关键的代码,创建Array,然后ps.setArray(parameterIndex, array)就可以了
Connection conn = ps.getConnection();
Array array = conn.createArrayOf(typeName, parameter);
ps.setArray(parameterIndex, array);

}

/**
* 获取可以为null的结果集
*
* @param resultSet 结果集
* @param columnName 列名
*/
@Override
public Object[] getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
return getArray(resultSet.getArray(columnName));
}


/**
* 获取可以为null的结果集
*
* @param resultSet 结果集
* @param columnIndex columnIndex 列标
*/
@Override
public Object[] getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
return getArray(resultSet.getArray(columnIndex));
}

/**
* 获取可以为null的结果集
*
* @param callableStatement CallableStatement
* @param paramIndex 列号
*/
@Override
public Object[] getNullableResult(CallableStatement callableStatement, int paramIndex) throws SQLException {
return getArray(callableStatement.getArray(paramIndex));
}

/**
* 将ARRAY 转为 java数组
*
* @param array 数组
* @return
*/
private Object[] getArray(Array array) {
if (array == null) {
return null;
}
try {
return (Object[]) array.getArray();
} catch (Exception e) {
logger.error("ARRAY转为java数组出错", e);
}
return null;
}

2、接下来在项目中使用TypeHandler使用

1)最简单粗暴的方式为在xml文件中直接指定TypeHandler

1
username_arr=#{username_arr,typeHandler=xxx.xxx.ArrayTypeHandler}::VARCHAR[]

2)在项目中引入TypeHandler

在mybatis的配置文件中添加typeHandler标签 如下所示

1
2
3
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>

然后在自定义Typehandler上添加@MappedTypes和@MappedJdbcTypes注解。例如对于数组类型的TypeHandler,如下所示

1
2
@MappedTypes({Integer[].class,String[].class,Boolean[].class,Number[].class})
@MappedJdbcTypes(value = {JdbcType.ARRAY},includeNullJdbcType = true)

然后在对应字段上就可以自动使用typeHandler了,例如用户名数组为位String数组,对应到数据库上就是varchar[],就可以如下使用

1
username_arr=#{username_arr}::VARCHAR[]

12

ascetic

瑶瑶宝宝最可爱

14 日志
8 分类
11 标签
© 2025 ascetic
由 Hexo 强力驱动
|
主题 — NexT.Mist v5.1.4