java多线程之线程共享(二)——synchronized、volatile、ThreadLocal

本文探讨了Java多线程中的同步机制,详细讲解了synchronized内置锁的原理和用法,包括对象锁和类锁的区别。接着分析了volatile关键字的作用,它确保变量的可见性但不保证原子性。最后,介绍了ThreadLocal实现线程隔离的机制,以及它的应用场景和内存消耗。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近有在跟着某讯课堂学习架构师模块,先系统化的回顾一下多线程相关的知识,之后会持续更新。

synchronized内置锁

定义:线程进入同步代码块或方法的时候会自动获得锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。

对象锁,锁的是类的对象实例。
对象锁有两种方式

private synchronized   void  test1(){     //第一种方式,方法上加synchronized   
}

private    void  test1(){
    synchronized(this){                   //第二种方式,this方式
    }
}

需要注意的是,当下面传入的object对象不同的话,两个线程是可以同时进行的。只有同一个的new实例才能先后执行

    SynClzAndInst synClzAndInst = new SynClzAndInst();
Thread t1 = new Thread(new InstanceSyn(object));

SynClzAndInst synClzAndInst2 = new SynClzAndInst();
Thread t2 = new Thread(new Instance2Syn(object));

t1.start();
t2.start();

类锁,锁的是每个类的的Class对象,每个类的的Class对象在一个虚拟机中只有一个,所以类锁也只有一个。

    private static synchronized  void  test1(){
        System.out.println("类锁方式一");
    }
public void methodName2() {
synchronized (TestLock.class) {
System.out.println("类锁方式二");
}

volatile关键字

最轻量的同步机制,效率高,只能保证可见性。这个涉及到线程的内存模型
在这里插入图片描述
每个线程都有自己的线程内存,和一个共享的主内存,一般线程对变量的操作都在自己的工作内存中进行,不能直接读写主内存,不同线程之间不能访问别的线程的工作内存内的变量(只能通过主内存来传递)

volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主内存中读取。
volatile仅能使用在变量级别

但不能保证原子性(线程A修改了变量还没结束时,另外的线程B在主内存可以看到已修改的值,而且可以修改这个变量,而不用等待A释放锁,因为Volatile 变量没上锁),举个栗子

    private volatile  int age =10;
    public int getAge(){
        return  age;
    }
    public void setAge(){
        age =age+1;
    }

上面执行加操作还未完成的时候,另一个线程获取到主内存中的age值,对其进行了修改,再更新到主内存,恰好此时栗子中的加操作执行完成,同步到主内存,此时主内存中留存到最后的值肯定不是我们期待的。
volatile 性能
volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行
使用场景
适合于只有一个线程写,多个线程读的场景,因为它只能确保可见性

ThreadLocal

先上代码,定义一个Integer类型的变量local

   static  ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
            @Override
            protected Integer initialValue() {
                return 1;
            }
        };

先来看一段官方的解释
“该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。”

我的理解
1、ThreadLocal提供了一种访问某个变量的特殊方式:访问到的变量属于当前线程,即保证每个线程的变量不一样,而同一个线程在任何地方拿到的变量都是一致的,这就是所谓的线程隔离。
2、如果要使用ThreadLocal,通常定义为private static类型,在我看来最好是定义为private static final类型。

它和volatile恰好是有些相反的,可以理解为是个map,类型 Map<Thread,Integer>,存储的是线程和对应的变量副本,即每个线程都保有一份变量的副本(因此也比较占用内存)。它提供了以下几个方法

   local.get(); //获取当前线程的变量副本值
   local.set(9);//重新设置当前线程的变量副本值
   local.remove();//移除值,得到null
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值