使Java设置线程安全

时间:2016-12-18 11:38:15

标签: java android thread-safety android-volley

我正在更新Volley onResponse回调中的一个集合,如下所示:

@Override
public void onResponse(String response)
{
    if (!response.equals(Properties.PRODUCT_NOT_FOUND) || !response.equals(Properties.USER_NOT_FOUND))
    {
        if (user.getFavoriteProducts().contains(product.getId()))
        {
            user.getFavoriteProducts().remove(product.getId());

        } else {
            user.getFavoriteProducts().add(product.getId());
        }

        mSharedPreferencesManager.insertUser(user);
    }
}

当同时收到两个响应时,同时加入该组。我试图让这个设置线程安全,但我无法让它工作,这是我迄今为止所尝试的:

使用SynchronizedSet:

user.setFavoriteProducts(Collections.synchronizedSet(new HashSet<Long>()));

使用CopyOnWriteArraySet:

user.setFavoriteProducts(new CopyOnWriteArraySet<Long>());

同步回调中​​的代码:

private static final Object object = new Object();

@Override
public void onResponse(String response)
{
    synchronized (object)
    {
        if (!response.equals(Properties.PRODUCT_NOT_FOUND) || !response.equals(Properties.USER_NOT_FOUND))
        {
            if (user.getFavoriteProducts().contains(product.getId()))
            {
                user.getFavoriteProducts().remove(product.getId());

            } else {
                user.getFavoriteProducts().add(product.getId());
            }

            mSharedPreferencesManager.insertUser(user);
        }
    }
}

这些都没有奏效,任何帮助都会受到赞赏!

编辑:什么不起作用的是只插入了一个项目。

编辑2 :我已经尝试了N0un的方法,但仍然只插入了一个项目

这是我使用的代码:

@Override
public void onResponse(final String response)
{
    Log.d(Properties.TAG, "[REST_CLIENT] Response received: " + response);

    AsyncTask.execute(new Runnable()
    {
        @Override
        public void run()
        {
            synchronized (RestClient.class)
            {
                if (!response.equals(Properties.PRODUCT_NOT_FOUND) || !response.equals(Properties.USER_NOT_FOUND))
                {
                    if (user.getFavoriteProducts().contains(product.getId()))
                    {
                        Log.d(Properties.TAG, "[REST_CLIENT] Removing product from favorites: " + product.getId());
                        user.getFavoriteProducts().remove(product.getId());

                    } else {
                        Log.d(Properties.TAG, "[REST_CLIENT] Adding product to favorites: " + product.getId());
                        user.getFavoriteProducts().add(product.getId());
                    }

                    Log.d(Properties.TAG, "[REST_CLIENT] Updating user");
                    mSharedPreferencesManager.insertUser(user);

                    Log.d(Properties.TAG, "[REST_CLIENT] Set size: " + user.getFavoriteProducts().size());
                }
            }
        }
    });
}

这些是我得到的日志:

D/CUOKA: [REST_CLIENT] Response received: ACCEPTED
D/CUOKA: [REST_CLIENT] Adding product to favorites: 3921
D/CUOKA: [REST_CLIENT] Updating user
D/CUOKA: [REST_CLIENT] Response received: ACCEPTED
D/CUOKA: [REST_CLIENT] Set size: 1
D/CUOKA: [REST_CLIENT] Adding product to favorites: 2361
D/CUOKA: [REST_CLIENT] Updating user
D/CUOKA: [REST_CLIENT] Set size: 1

编辑3 :这是将用户插入SharedPreferences的代码:

public synchronized boolean insertUser(final User user)
{
    mEditor = mSharedPreferences.edit();

    Gson gson = new Gson();
    String json = gson.toJson(user);

    mEditor.putString(KEY_USER, json);

    return mEditor.commit();
}

2 个答案:

答案 0 :(得分:0)

好吧,使用经典Java Executor在UI线程中调用Volley的侦听器。我认为有一种机制可以在UI线程中有太多工作时取消一些监听器调用。

试试:

@Override
public void onResponse(String response)
{
    AsyncTask.execute(new Runnable() {

        @Override
        public void run()
        {
            synchronized (MyClassName.class) {
                if (!response.equals(Properties.PRODUCT_NOT_FOUND)
                        || !response.equals(Properties.USER_NOT_FOUND)) {
                    if (user.getFavoriteProducts().contains(product.getId())) {
                        user.getFavoriteProducts().remove(product.getId());

                    } else {
                        user.getFavoriteProducts().add(product.getId());
                    }

                    mSharedPreferencesManager.insertUser(user);
                }
            }
        }
    });
}

请参阅以下回复:herehere以获取更多解释。

  

只需将在侦听器中执行的数据处理移动到后台线程/ AsyncTask即可释放UI线程并防止阻塞。

编辑:经过一些讨论和代码审核后,我发现了(第二个)问题:在请求之前检索到user并且线程安全问题就在这里。新请求正在进行时,user的数据尚未保存。因此,第二个请求与第一个请求相同Set,而不是新更新的Set。所以user应该在我建议的synchronized区块中检索,然后再进行其他处理。

答案 1 :(得分:-1)

监听器正在使用Main Thread(UI线程),因此可能会发生某种阻塞。您应该将数据处理移动到后台线程或使处理异步。

关于设置同步,您应该使用ConcurentHashMap,并使用集合中的newSetFromMap()方法简单地包装Set。