线程间的共享:

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,分析内存泄漏)------------------------------------------------------------

相关文章:

  • 2022-12-23
  • 2021-09-25
  • 2022-12-23
  • 2022-01-01
  • 2021-12-26
  • 2021-12-28
猜你喜欢
  • 2021-06-16
  • 2022-12-23
  • 2021-12-27
  • 2021-10-30
  • 2022-12-23
相关资源
相似解决方案