synchronized 关键字用法

synchronized 关键字最主要的三种使用方式:

1.修饰实例⽅法: 作⽤于当前对象实例加锁,进⼊同步代码前要获得当前对象实例的锁

synchronized void method() {
 //业务代码
}

2.修饰静态⽅法: 也就是给当前类加锁,会作⽤于类的所有对象实例 ,进⼊同步代码前要获得
前 class 的锁
。因为静态成员不属于任何⼀个实例对象,是类成员( static 表明这是该类的⼀个
静态资源,不管 new 了多少个对象,只有⼀份)。所以,如果⼀个线程 A 调⽤⼀个实例对象的
⾮静态 synchronized ⽅法,⽽线程 B 需要调⽤这个实例对象所属类的静态 synchronized ⽅法,
是允许的,不会发⽣互斥现象,因为访问静态 synchronized ⽅法占⽤的锁是当前类的锁,⽽访
问⾮静态 synchronized ⽅法占⽤的锁是当前实例对象锁

synchronized void staic method() {
 //业务代码
}

3.修饰代码块 :指定加锁对象,对给定对象/类加锁。 synchronized(this|object) 表示进⼊同步代码
库前要获得给定对象的锁。 synchronized(.class) 表示进⼊同步代码前要获得当前 class 的锁

synchronized(this) {
 //业务代码
}

总结

  • synchronized 关键字加到 static 静态⽅法和 synchronized(class) 代码块上都是是给 Class
    类上锁。
  • synchronized 关键字加到实例⽅法上是给对象实例上锁。
  • 尽量不要使⽤ synchronized(String a) 因为 JVM 中,字符串常量池具有缓存功能。

双重检验锁方式实现单例模式的原理

双重校验锁实现对象单例(线程安全)

public class Singleton {
 	private volatile static Singleton uniqueInstance;
 	
 	private Singleton() {
 	}
 	
 	public static Singleton getUniqueInstance() {
 	//先判断对象是否已经实例过,没有实例化过才进⼊加锁代码
 		if (uniqueInstance == null) {
 		//类对象加锁
 			synchronized (Singleton.class) {
 		if (uniqueInstance == null) {
 			uniqueInstance = new Singleton();
 			}
 		}
 	}
	 return uniqueInstance;
 }
}
  • 另外,需要注意 uniqueInstance 采⽤ volatile 关键字修饰也是很有必要。uniqueInstance 采⽤ volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton(); 这段代码其实是分为三步执⾏:
  1. 为 uniqueInstance 分配内存空间
  2. 初始化 uniqueInstance
  3. 将 uniqueInstance 指向分配的内存地址
  • 但是由于 JVM 具有指令重排的特性,执⾏顺序有可能变成 1->3->2。指令重排在单线程环境下不
    会出现问题,但是在多线程环境下会导致⼀个线程获得还没有初始化的实例。例如,线程 T1 执
    ⾏了 1 和 3,此时 T2 调⽤ getUniqueInstance () 后发现 uniqueInstance 不为空,因此返回
    uniqueInstance ,但此时 uniqueInstance 还未被初始化。
  • 使⽤ volatile 可以禁⽌ JVM 的指令重排,保证在多线程环境下也能正常运⾏。

synchronized 关键字和 volatile 关键字的区别

synchronized 关键字和 volatile 关键字是两个互补的存在,⽽不是对⽴的存在!

  • volatile 关键字是线程同步的轻量级实现,所以 volatile 性能肯定⽐ synchronized 关键字要好。但是 volatile 关键字只能⽤于变量⽽ synchronized 关键字可以修饰⽅法以及代码块。
  • volatile 关键字能保证数据的可⻅性,但不能保证数据的原⼦性。 synchronized 关键字两
    者都能保证。
  • volatile 关键字主要⽤于解决变量在多个线程之间的可⻅性,⽽ synchronized 关键字解决
    的是多个线程之间访问资源的同步性。

ThreadLocal

通常情况下,我们创建的变量是可以被任何⼀个线程访问并修改的。如果想实现每⼀个线程都有
自己的专属本地变量该如何解决呢
? JDK 中提供的 ThreadLocal 类正是为了解决这样的问题。
ThreadLocal 类主要解决的就是让每个线程绑定⾃⼰的值,可以将 ThreadLocal 类形象的⽐喻成
存放数据的盒⼦,盒⼦中可以存储每个线程的私有数据

如果你创建了⼀个 ThreadLocal 变量,那么访问这个变量的每个线程都会有这个变量的本地副
,这也是 ThreadLocal 变量名的由来。他们可以使⽤ get()和 set() ⽅法来获取默认值
或将其值更改为当前线程所存的副本的值,从⽽避免了线程安全问题。

举个简单的例⼦:
⽐如有两个⼈去宝屋收集宝物,这两个共⽤⼀个袋⼦的话肯定会产⽣争执,但是给他们两个⼈每
个⼈分配⼀个袋⼦的话就不会出现这样的问题。如果把这两个⼈⽐作线程的话,那么ThreadLocal 就是⽤来避免这两个线程竞争的。

ThreadLocal 原理

public class Thread implements Runnable {
 ......
//与此线程有关的ThreadLocal值。由ThreadLocal类维护
ThreadLocal.ThreadLocalMap threadLocals = null;
//与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
 ......
}

从上⾯ Thread 类 源代码可以看出 Thread 类中有⼀个 threadLocals 和 ⼀个inheritableThreadLocals 变量,它们都是 ThreadLocalMap 类型的变量,我们可以把ThreadLocalMap 理解为 ThreadLocal 类实现的定制化的 HashMap 。默认情况下这两个变量都是 null,只有当前线程调⽤ ThreadLocal 类的 set 或 get ⽅法时才创建它们,实际上调⽤这两个⽅法的时候,我们调⽤的是 ThreadLocalMap 类对应的 get() 、 set() ⽅法。

ThreadLocal 类的 set() ⽅法

public void set(T value) {
 	Thread t = Thread.currentThread();
 	ThreadLocalMap map = getMap(t);
 	if (map != null)
 		map.set(this, value);
	 else
 		createMap(t, value);
 }
 	ThreadLocalMap getMap(Thread t) {
 	return t.threadLocals;
 }

通过上⾯这些内容,我们⾜以通过猜测得出结论:最终的变量是放在了当前线程的ThreadLocalMap 中,并不是存在 ThreadLocal 上, ThreadLocal 可以理解为只是 ThreadLocalMap 的封装,传递了变量值。 ThrealLocal 类中可以通过 Thread.currentThread()获取到当前线程对象后,直接通过 getMap(Thread t) 可以访问到该线程的 ThreadLocalMap 对象。

ThreadLocal 内部维护的是⼀个类似 Map 的 ThreadLocalMap 数据结构, key 为当前对象的 Thread 对象,值为 Object 对象。

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
 ......
}

⽐如我们在同⼀个线程中声明了两个 ThreadLocal 对象的话,会使⽤ Thread 内部都是使⽤仅有
那个 ThreadLocalMap 存放数据的, ThreadLocalMap 的 key 就是 ThreadLocal 对象,value 就是
ThreadLocal 对象调⽤ set ⽅法设置的值。
在这里插入图片描述

ThreadLocalMap 是 ThreadLocal 的静态内部类。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值