android线程同步实现方式

为什么需要线程同步? 当多个线程访问和修改共享资源(同一个变量、集合、文件等)时,如果没有正确的同步,可能会导致数据不一致、脏读、幻读等问题。同步的目的是确保在任一时刻,只有一个线程可以执行特定的代码段(临界区)。

一、 基本的Java同步机制

1、synchronized 关键字
最经典、最基础的同步方式。它提供了一种内置锁(Intrinsic Lock)或监视器锁(Monitor Lock),确保线程互斥地执行代码块或方法。

  • 同步代码块:更灵活,可以指定锁对象。
private final Object mLock = new Object(); // 专门的锁对象
private int mCount = 0;

public void increment() {
    synchronized (mLock) { // 线程进入这里会获取mLock的锁
        mCount++; // 临界区
    } // 线程执行完会自动释放锁
}
  • 同步实例方法:锁是当前对象实例(this)。
public synchronized void increment() {
    mCount++;
}
  • 同步静态方法:锁是当前类的Class对象。
public static synchronized void staticIncrement() {
    staticCount++;
}

2、volatile 关键字
确保变量的可见性,但不保证操作的原子性。
作用:当一个线程修改了volatile变量,新值会立即被刷新到主内存。当其他线程读取该变量时,它会从主内存重新加载,而不是使用自己工作内存中的缓存值。
适用场景:标志位(boolean flag),或者该变量不参与依赖于其当前值的复合操作(例如 count++ 不是原子操作,volatile 无法保证其线程安全)。

private volatile boolean isRunning = false;

public void start() {
    isRunning = true;
    // 其他线程可以立即看到isRunning变为true
}

二、 java.util.concurrent (JUC) 包中的高级工具

这是Java提供的更强大、更现代、性能更高的并发工具包。
1、ReentrantLock (可重入锁)
synchronized的增强版,提供了更多高级功能。

  • 基本用法:
private final ReentrantLock mLock = new ReentrantLock();
private int mCount = 0;

public void increment() {
    mLock.lock(); // 获取锁
    try {
        mCount++; // 临界区
    } finally {
        mLock.unlock(); // 必须在finally块中释放锁!
    }
}

2、CountDownLatch (倒计时门闩)
允许一个或多个线程等待其他线程完成操作。
典型场景:主线程等待所有子线程初始化完成。

// 主线程
CountDownLatch latch = new CountDownLatch(3); // 计数器初始化为3

// 启动3个工作线程,每个线程完成任务后调用 latch.countDown()
startWorkerThread(latch);
startWorkerThread(latch);
startWorkerThread(latch);

latch.await(); // 主线程在此阻塞,直到计数器减为0
proceed(); // 所有工作线程完成后,继续执行

// 工作线程
public void run() {
    try {
        // ... 执行任务
    } finally {
        latch.countDown(); // 完成后将计数器减1
    }
}

3、Semaphore (信号量)
控制同时访问某个特定资源的线程数量,用于流量控制。
典型场景:限制同时下载的线程数、数据库连接池。

private final Semaphore mDownloadSemaphore = new Semaphore(3); // 只允许3个线程同时执行

public void startDownload() {
    try {
        mDownloadSemaphore.acquire(); // 获取一个许可
        // ... 执行下载任务(临界区)
    } finally {
        mDownloadSemaphore.release(); // 释放许可
    }
}

三、 Android特有的机制

1、Handler + Looper + MessageQueue
这是Android最核心的线程间通信机制,本质上是一个单线程消息处理模型。它通过将任务(Message或Runnable)投递到目标线程的MessageQueue中,由该线程的Looper按顺序取出并执行,从而实现了将代码切换到特定线程(通常是主线程)执行的效果,避免了多线程并发访问UI的问题。

  • 作用:线程切换和任务同步。
// 在后台线程中
Handler mainHandler = new Handler(Looper.getMainLooper()); // 获取主线程的Handler
mainHandler.post(new Runnable() {
    @Override
    public void run() {
        // 这段代码将在主线程执行,可以安全更新UI
        mTextView.setText("Update from background thread");
    }
});

2、AsyncTask (已废弃)

底层基于Handler和线程池。它被设计用来执行短暂的异步任务并在完成后更新UI。注意:在Android 11 (API 30) 中已被正式废弃,推荐使用 kotlin协程、RxJava 或 ListenableFuture。

四、 现代Kotlin推荐方式

1、Mutex (互斥锁)

Kotlin协程世界中的ReentrantLock,但更轻量、更协程友好。它使用lock和unlock,但更推荐使用withLock扩展函数。

val mutex = Mutex()
var counter = 0

fun increment() = runBlocking {
    mutex.withLock { // 挂起协程,而不是阻塞线程
        counter++
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值