【问题标题】:how to protect a method from parallel access with synchronization(){} block in android如何使用android中的同步(){}块保护方法免受并行访问
【发布时间】:2017-09-24 17:36:23
【问题描述】:

我有以下活动:

public class FaceActivity extends BiometricActivity implements CameraControlsListener

本次活动的onCreate方法如下:

controlsView = new CameraControlsView(this, this);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);

            controlsView.setLayoutParams(params);
            layout.addView(controlsView);

CameraControlsView 类如下:

public class CameraControlsView extends LinearLayout {

    // ===========================================================
    // Public types
    // ===========================================================

    public interface CameraControlsListener {
        void onSwitchCamera();

        void onChangeFormat();
    }

    // ===========================================================
    // Private fields
    // ===========================================================

    private CameraControlsListener mListener;

    // ===========================================================
    // Public constructor
    // ===========================================================

    public CameraControlsView(Context context, CameraControlsListener listener) {
        super(context);
        if (listener == null) throw new NullPointerException("listener");
        mListener = listener;
        LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = mInflater.inflate(R.layout.camera_controls, this, true);
        ImageView switchCamera = (ImageView) view.findViewById(R.id.switch_camera);
        switchCamera.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                synchronized (this) {
                    mListener.onSwitchCamera();
                }
            }
        });

        ImageView changeFormat = (ImageView) view.findViewById(R.id.change_format);
        changeFormat.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mListener.onChangeFormat();
            }
        });
    }

}

FaceAcivity 中重写了 onSwitchCamera 函数,如下所示:

@Override
    public void onSwitchCamera() {
        if (!FacePreferences.isUseLiveness(this)) {
            cancel();
            NCamera currentCamera = client.getFaceCaptureDevice();
            for (NDevice device : client.getDeviceManager().getDevices()) {
                if (device.getDeviceType().contains(NDeviceType.CAMERA)) {
                    if (!device.equals(currentCamera)) {
                        client.setFaceCaptureDevice((NCamera) device);
                        startCapturing();
                        break;
                    }
                }
            }
        }

    }

当我按下 switchCamera 按钮时,onSwitchCamera 函数被调用。当我多次单击它,而不是等待相机更改并开始捕捉时,可能会发生一些赛车情况,然后以某种方式未显示相机输出。所以我想保护该方法免受同步访问的并行访问(){}块。

我在 switchCamera 按钮的 onClick 方法中保留了同步关键字,但它不起作用。如何保护方法免受并行访问?

【问题讨论】:

  • 如果不需要并发,为什么不使用布尔值等待第一个操作完成?
  • 你能不能不禁用switchCamera button,让它不能被多次按下?
  • 我想通过同步(){} 块保护方法免受并行访问
  • 那你为什么不像public synchronized void onSwitchCamera() { }那样简单地同步方法。
  • 同步块或方法会阻塞 UI 线程。因此不建议您的情况。

标签: java android multithreading synchronized


【解决方案1】:

我想指出,任何“同步”块都会影响性能,因为它本身需要时间,并且会锁定其他线程访问代码范围。此外,“同步”块导致锁定在大括号中的对象实例。 "synchronized(this)" 是一个不好的模式,因为锁定了整个实例。"Synchronized" 块用于控制对资源的访问,这些资源可以被线程并发访问,并且必须提供它们的更改隔离。 所以你的

synchronized (this) {
    mListener.onSwitchCamera();                
}

考虑到它被注入构造函数并且没有更改时,两次似乎是不合理的。首先-同步(this)基本上不会阻止其他任何人访问侦听器:在您的情况下,“this”是内联(lambda)类的一个实例,除了事件源(您的操作系统,如我所见,是Android,任何布局都在UI线程中工作)。 其次-您尝试同步对实例的访问,该实例仅在调用其函数时不可更改(????)。不会发生任何竞争条件,因为函数在其实例初始化之前绑定到类。如果你想同步访问Listener的资源,那么你必须在类的实例中进行,在同一个void onSwitchCamera()中。比如这样

 @Override
 public void onSwitchCamera() 
 {
     if (!FacePreferences.isUseLiveness(this)) {
         cancel();
         synchronized(client)
         {
             NCamera currentCamera = client.getFaceCaptureDevice();
             for (NDevice device : client.getDeviceManager().getDevices()) {
                 if (device.getDeviceType().contains(NDeviceType.CAMERA)) {
                     if (!device.equals(currentCamera)) {
                         client.setFaceCaptureDevice((NCamera) device);
                         startCapturing();
                         break;
                         }
                    }
                }
            }
        }

    }

但你必须只锁定实例,这些实例处于竞争状态的危险中,并且仅在危险存在的时间段内。

【讨论】:

    【解决方案2】:

    一般来说,您应该检查相机回调:通过相机回调(onActive、onClosed),您将知道何时启用按钮有意义。

    您尝试使用 synchronized 的方式实际上没有意义,因为您需要同步,但 不是 意味着从不同线程同时访问相同的“软件”对象。需要与摄像头驱动同步。

    我的意思是两次启用相机没有意义,只需等到它完成并提供再次禁用它的选项即可。

    搜索您的createCaptureSession() 调用并在CameraCaptureSession.StateCallback 中添加处理按钮的启用状态。

    【讨论】:

      猜你喜欢
      • 2019-08-18
      • 2020-07-26
      • 1970-01-01
      • 1970-01-01
      • 2012-01-12
      • 1970-01-01
      • 1970-01-01
      • 2016-05-26
      • 2016-05-06
      相关资源
      最近更新 更多