Java线程同步笔记

本文介绍了多线程环境下线程同步的多种实现方式,包括synchronized关键字、ReentrantLock、volatile变量、ThreadLocal、阻塞队列及原子变量等。通过具体示例说明了不同方法的应用场景。

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

线程同步

多线程情况下,当一个线程使用某一资源时,其他线程无法同时使用此资源,直到当前线程释放对资源的占用。例如A线程使用对象X时,其他线程都无法操作X,直到A线程使用完毕。

https://www.cnblogs.com/XHJT/p/3897440.html

实现方式:

1、通过synchronized关键字修饰方法或者代码块:

//同步普通方法
//同步静态方法时,会锁住整个类
public synchronized void testA() {
    //do something...
} 

2、使用synchronized变量锁定资源,修饰代码块 ###

例如:

public void add(int n)  {
    synchronized(obj){
        sum = sum + n;
        try{Thread.sleep(10);}catch(Exception e){}
        System.out.println("sum="+sum);
    }
}  

注:同步是一种高开销的操作,因此应该尽量减少同步的内容。
通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

实例:银行取钱

http://blog.youkuaiyun.com/taoxiuxia/article/details/49157159

public class BankLockTest {
//账户余额
public int account = 1000 ;
//取钱,耗时操作(模拟
public void getMoney() {
    account -= 100 ;
    try {
        Thread.sleep(10);
        System.out.println("getMony后: " + account);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
public  class GetMoneyRun implements Runnable {
    BankLockTest bankLockTest = new BankLockTest() ;
    @Override
    public void run() {
        for (int i=0;i<4;i++) {
            bankLockTest.getMoney();
        }
    }
}
public static void main(String[] arg ) {
    GetMoneyRun runMoney =  new BankLockTest().new GetMoneyRun();
    Thread a = new Thread(runMoney);
    Thread b = new Thread(runMoney) ;
    a.start();
    b.start();
}

}

3、使用特殊域变量volatile实现线程同步

volatile为域变量提供了一种免锁机制,相当于告诉虚拟机这个域可能会被其他线程更新,因此每次使用该域时就要重新计算,而不是取寄存器里的值,volatile不提供任何原子操作,也不能修饰final变量

4、使用ReentrantLock实现Lock接口的锁

在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。

public Lock mLock = new ReentrantLock() ;
//取钱,耗时操作(模拟
public void getMoney() {
    mLock.lock();
    account -= 100 ;
    try {
        Thread.sleep(10);
        System.out.println("getMony后: " + account);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        mLock.unlock();
    }
}

ReentrantLock和synchronized关键字有相同的基本行为和语义,并提供了扩展,通过lock和unlock来锁住/解锁代码块

注:关于Lock对象和synchronized关键字的选择:
a.最好两个都不用,使用一种java.util.concurrent包提供的机制,
能够帮助用户处理所有与锁相关的代码。
b.如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码
c.如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁

5、使用局部变量实现线程同步

ThreadLocal管理变量,每个线程中都有个ThreadLocalMap,保存当前的Thread和变量值,线程使用的是变量的副本值,随意修改都不会影响其他线程。

ThreadLocal() : 创建一个线程本地变量 
get() : 返回此线程局部变量的当前线程副本中的值 
initialValue() : 返回此线程局部变量的当前线程的"初始值" 
set(T value) : 将此线程局部变量的当前线程副本中的值设置为value

注:ThreadLocal与同步机制
a.ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。
b.前者采用以”空间换时间”的方法(拿到副本即可更新,不需等待其他线程),后者采用以”时间换空间”的方式(其他线程等待变量被用完)

6、使用阻塞队列

LinkedBlockingQueue是一个基于已连接节点的,范围任意的阻塞队列,先进先出FIFO

LinkedBlockingQueue() : 创建一个容量为Integer.MAX_VALUE的LinkedBlockingQueue 
put(E e) : 在队尾添加一个元素,如果队列满则阻塞 
size() : 返回队列中的元素个数 
take() : 移除并返回队头元素,如果队列空则阻塞

注:BlockingQueue定义了阻塞队列的常用方法,尤其是三种添加元素的方法,我们要多加注意,当队列满时:
add()方法会抛出异常
offer()方法返回false
put()方法会阻塞

7、使用原子变量实现线程同步

原子操作指将对变量的读取、修改、保存操作看做一个整体,要么不完成,要么同时完成。例如可以用AtomicInteger实现以原子方式更新int的值。

对于引用变量和大多数原始变量(long和double除外)的读写操作;
对于所有使用volatile修饰的变量(包括long和double)的读写操作。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值