Android开发中多线程的使用注意事项

283 阅读3分钟

在多线程环境中,确保对共享资源(如 mTextureView)的访问是线程安全的非常重要。如果多个线程同时访问和修改同一个对象,可能会导致数据不一致、崩溃或者其他潜在的问题。为了保证线程安全,可以使用 synchronized 关键字来确保在同一时刻只有一个线程可以访问共享资源,或者使用更高级的并发控制工具(如 ReentrantLock)。

以下是使用 synchronizedReentrantLock 来确保 mTextureView 线程安全的代码示例。

示例 1:使用 synchronized 来确保线程安全

synchronized 可以用于方法或代码块,它通过锁定对象来保证每次只有一个线程可以进入该代码块。对于共享的资源 mTextureView,你可以在访问它的地方使用 synchronized 来确保只有一个线程可以同时操作它。

1.1 使用 synchronized 方法

javaCopy Code
public class FaceCapturedRunnable implements Runnable {
    private TextureView mTextureView;

    public FaceCapturedRunnable(TextureView textureView) {
        this.mTextureView = textureView;
    }

    @Override
    public void run() {
        synchronized (mTextureView) {
            // 确保对 mTextureView 的操作是线程安全的
            if (mTextureView != null && mTextureView.getBitmap() != null) {
                Bitmap bitmap = mTextureView.getBitmap();
                // 处理 bitmap
                Log.d("FaceCapturedRunnable", "Processing bitmap...");
                // 继续其他处理
            } else {
                Log.e("FaceCapturedRunnable", "Bitmap or mTextureView is null");
            }
        }
    }
}

1.2 使用 synchronized 代码块

如果你只需要保护某个代码块,而不是整个方法,可以使用 synchronized 代码块。以下代码只锁定 mTextureView 相关的部分。

javaCopy Code
public class FaceCapturedRunnable implements Runnable {
    private TextureView mTextureView;

    public FaceCapturedRunnable(TextureView textureView) {
        this.mTextureView = textureView;
    }

    @Override
    public void run() {
        if (mTextureView != null) {
            synchronized (mTextureView) {
                // 确保对 mTextureView 的操作是线程安全的
                if (mTextureView.getBitmap() != null) {
                    Bitmap bitmap = mTextureView.getBitmap();
                    // 处理 bitmap
                    Log.d("FaceCapturedRunnable", "Processing bitmap...");
                    // 继续其他处理
                } else {
                    Log.e("FaceCapturedRunnable", "Bitmap is null");
                }
            }
        } else {
            Log.e("FaceCapturedRunnable", "mTextureView is null");
        }
    }
}

示例 2:使用 ReentrantLock 来确保线程安全

ReentrantLock 提供了比 synchronized 更灵活的锁机制,允许你控制锁的获取和释放,也支持尝试获取锁等高级功能。你可以使用 ReentrantLock 来替代 synchronized

2.1 使用 ReentrantLock

javaCopy Code
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class FaceCapturedRunnable implements Runnable {
    private TextureView mTextureView;
    private Lock lock = new ReentrantLock();  // 创建一个 ReentrantLock 实例

    public FaceCapturedRunnable(TextureView textureView) {
        this.mTextureView = textureView;
    }

    @Override
    public void run() {
        lock.lock();  // 获取锁
        try {
            if (mTextureView != null && mTextureView.getBitmap() != null) {
                Bitmap bitmap = mTextureView.getBitmap();
                // 处理 bitmap
                Log.d("FaceCapturedRunnable", "Processing bitmap...");
                // 继续其他处理
            } else {
                Log.e("FaceCapturedRunnable", "Bitmap or mTextureView is null");
            }
        } finally {
            lock.unlock();  // 确保锁释放
        }
    }
}

在这个例子中,lock.lock() 会锁定 lock 对象,直到 unlock() 被调用。在锁保护的区域中,只有当前持有锁的线程可以执行相应的操作。finally 代码块确保了无论代码执行成功还是抛出异常,都会释放锁。

选择 synchronized 或 ReentrantLock

  • 如果只是简单地需要同步一个方法或代码块,并且没有复杂的锁管理需求,可以使用 synchronized
  • 如果你需要更多的控制,如尝试锁定、锁超时或者使用多个锁对象等,ReentrantLock 是一个更灵活的选择。

总结

  • 使用 synchronized 或 ReentrantLock 来保护对共享资源(如 mTextureView)的访问。
  • synchronized 适用于简单的场景,它可以确保同一时间只有一个线程执行某个方法或代码块。
  • ReentrantLock 提供了更细粒度的控制,可以在需要时显式地锁定和解锁,适用于更复杂的并发需求。

这两种方法都能有效地保证多线程环境中的线程安全,避免了因资源争用或竞争导致的数据不一致问题。