线程间的共享:
synchronized内置锁
线程开始运行,拥有自己的栈空间,就如同一个脚本一样,按照既定的代码一步一步的执行,直到终止。但是,每个运行中的线程,如果仅仅是孤立的运行,那么没有一点价值,或者说价值很少,如果多个线程能够相互配合完成工作,包括数据之间的共享,写统处理事情。这将会带来巨大的价值。
Java支持多个线程同时访问一个对象或者对象的成员变量,关键字synchronized可以修饰方法或者以同步块的形式来执行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性,又称为内置锁机制。
对象锁和类锁:
对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。
但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,类锁其实锁的是每个类的对应的class对象。类锁和对象锁之间也是互不干扰的。
Volatile,最轻量的同步机制(volatile适用的场景:一个线程写,多个线程读)
Volatile保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
不加 volatile 时,子线程无法感知主线程修改了 ready 的值,从而不会退出循环, 而加了 volatile 后,子线程可以感知主线程修改了 ready 的值,迅速退出循环。 但是 volatile 不能保证数据在多个线程下同时写时的线程安全,
ThreadLocal与synchonized比较
ThreadLocal和synchonized都用于解决多线程并发访问。可是ThreadLocal与Synchonized有本质的区别。例:
- Synchonized是利用锁的机制,使变量或代码块在某一时刻该仅仅能被一个线程访问。
- 而ThreadLocal为每个线程都提供了变量的副本,使得每个线程在某一时间访问到的并非同一个对象,这个就隔离了多个线程对数据的数据共享。
Spring的事务就借助了ThreadLocal类。Spring会从数据库连接池中获得一个connection,然后把connection放进ThreadLocal中,也就和线程绑定了,事务需要提交或者回滚,只要从ThreadLocal中拿到connection进行操作。为何Spring的事务要借助ThreadLocal类?
以 JDBC 为例,正常的事务代码可能如下:
dbc=newDataBaseConnection();//第1 行
Connectioncon=dbc.getConnection();//第 2 行
con.setAutoCommit(false);////第 3 行
con.executeUpdate(...);//第 4 行
con.executeUpdate(...);//第 5 行
con.executeUpdate(...);//第 6 行
con.commit();////第 7 行
上述代码,可以分成三个部分:
事务准备阶段:第 1~3 行
业务处理阶段:第 4~6 行
事务提交阶段:第 7 行
可以看出,不管开启事务还是执行具体的sql都需要一个数据库连接。
现在我们开发应用一般都采用三层结构,如果我们控制事务的代码都放在 DAO(DataAccessObject)对象中,在 DAO 对象的每个方法当中去打开事务和关闭 事务,当 Service 对象在调用 DAO 时,如果只调用一个 DAO,那我们这样实现则 效果不错,但往往我们的 Service 会调用一系列的 DAO 对数据库进行多次操作, 那么,这个时候我们就无法控制事务的边界了,因为实际应用当中,我们的Service 调用的 DAO 的个数是不确定的,可根据需求而变化,而且还可能出现 Service 调 用 Service 的情况。
ThreadLocal的使用
ThreadLocal的接口只有4个方法:
• void set(Object value)
设置当前线程的线程局部变量的值
• public Object get()
该方法返回当前线程所对应的线程局部变量。
• public void remove()
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法时jdk5.0新增的方法。需要指出的时,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显示调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
• protected Object initialValue()
返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的,这个方法是一个延迟调用的方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLock中的缺省实现直接返回一个null。
实现解析:
上面160行获取当前线程,然后调用161行的getMap()方法,获取对应的ThreadLocalMap,ThreadLocalMap是ThreadLocal的静态内部类,然后Thread类中有一个这样的类型成员182行,所以getMap是直接返回Thread的成员。
看下ThreadLocal的内部类ThreadLocalMap源码:
可以看到308行有个Entry内部静态类。他继承了WeakReference,它记录了两个信息,一个是ThreadLocal<?>类型,一个是Object类型的值。163行getEntry方法是获取某个ThreadLocal对应的值,set方法就是更新或赋值相应的ThreadLocal对应的值。
再看get方法,其实就是拿到每个线程独有的ThreadLocalMap
然后再用ThreadLocal的当前实例,拿到Map中相应的Entry,然后就可以拿到相应的值返回出去。如果Map为空,还是会先行map的创建,初始化等工作。
------------------------------------------------(下一节继续ThreadLocal,分析内存泄漏)------------------------------------------------------------