Java 代碼多線程問題

很多問題,自己測試可能測不出來,但是跑到了線上,就有可能出現問題,比如多線程問題。

錯誤堆棧:

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.get(ArrayList.java:437)
	at com.zhangyue.we.ad.model.AdSchedule.getValidPartnersAdSource(SourceFile:347)
	at com.zhangyue.module.ad.e.isPartnersAdTimeOn(SourceFile:177)
	at com.zhangyue.module.ad.e.processEvent(SourceFile:50)
	at com.zhangyue.iReader.module.driver.ad.a.loadAdStrategy(SourceFile:324)
	at com.zhangyue.iReader.module.proxy.AdProxy.loadAdStrategy(SourceFile:72)
	at com.chaozh.iReader.ui.activity.u.run(SourceFile:689)
	at java.lang.Thread.run(Thread.java:764)

有問題的代碼:


public class AdSchedule {
   
    private ArrayList<String> mAdResource;

    public boolean hasValidAd(String pos) {
        if (!isHaveRules()) {
            return false;
        }
        ArrayList<Bean> beans = body.rule.get(pos);
        if (beans != null) {
            mAdResource = new ArrayList<>();
            mAdResource.clear();
            for (Bean bean : beans) {
                if (bean.isValid()) {
                    mAdResource.add(bean.source);
                    return true;
                }
            }
        }
        return false;
    }

   
     *
     * @param pos 廣告顯示的位置
     * @return
     */
    public String getValidPartnersAdSource(String pos) {
        if (!isHaveRules()) {
            return "";
        }
        ArrayList<Bean> beans = body.rule.get(pos);
        if (beans != null) {
            mAdResource = new ArrayList<>();
            mAdResource.clear();
    
            int apkVersionCode = APK.getApkVersionCode(PluginRely.getAppContext());
            if (Util.isRunningInVivo(PluginRely.getAppContext()) && apkVersionCode >= 7150005 && apkVersionCode != 7150009) {

                for (Bean bean : beans) {
                    if (bean.isValid()) {
                        mAdResource.add(bean.source);
                        LOG.E(TAG, "Ad插件配置的廣告資源 source : " + bean.source);
                    }
                }
                StringBuilder result = new StringBuilder();
                for (int i = 0; i < mAdResource.size(); i++) {
                    result.append(mAdResource.get(i));
                    if (i != mAdResource.size()-1) {
                        result.append("|");
                    }
                }
                return result.toString();
            }else {
                for (Bean bean : beans) {
                    if (bean.isValid()) {
                        mAdResource.add(bean.source);
                        LOG.E(TAG, "Ad插件配置的廣告資源 source : " + bean.source);
                        return bean.source;
                    }
                }
            }
        }
        return "";
    }

    /**
     * 獲取當前要顯示的廣告資源。返回的是排期中第一個廣告資源
     *
     * @return
     */
    @JSONField(serialize = false)
    public String getAdResource() {
        if (mAdResource != null && mAdResource.size() > 0) {
            String source = mAdResource.get(0);
            return TextUtils.isEmpty(source) ? "三方廣告" : source;
        } else {
            return "三方廣告";
        }
    }

    
}

修改之後:



/**

 * @description
 */
public class AdSchedule {
    public final static String TAG = "ad_AdSchedule";
    public int code;
    public String msg;
    public BodyBean body;
    // 僅標識開屏位置的廣告源
    private ArrayList<String> mAdResource;

    
     *
     * @param pos 廣告顯示的位置
     * @return
     */
    public String getValidPartnersAdSource(String pos) {
        if (!isHaveRules()) {
            return "";
        }
        ArrayList<Bean> beans = body.rule.get(pos);
        if (beans != null) {
            List<String> adResource = new ArrayList<>();
            //vivo 項目 該方法返回一個列表  裏面有廣告補量的邏輯,如果第一個廣告展示不了,加載第二個,只在vivo 7.15.5以上版本返回,7.15.9上不返回,因爲7.15.9是7.15.3的複製品
            int apkVersionCode = APK.getApkVersionCode(PluginRely.getAppContext());
            if (Util.isRunningInVivo(PluginRely.getAppContext()) && apkVersionCode >= 7150005 && apkVersionCode != 7150009) {

                for (Bean bean : beans) {
                    if (bean.isValid()) {
                        adResource.add(bean.source);
                        LOG.E(TAG, "Ad插件配置的廣告資源 source : " + bean.source);
                    }
                }
                StringBuilder result = new StringBuilder();
                for (int i = 0; i < adResource.size(); i++) {
                    result.append(adResource.get(i));
                    if (i != adResource.size()-1) {
                        result.append("|");
                    }
                }
                return result.toString();
            }else {
                for (Bean bean : beans) {
                    if (bean.isValid()) {
                        adResource.add(bean.source);
                        LOG.E(TAG, "Ad插件配置的廣告資源 source : " + bean.source);
                        return bean.source;
                    }
                }
            }
        }
        return "";
    }

    /**
     * 獲取當前要顯示的廣告資源。返回的是排期中第一個廣告資源
     *
     * @return
     */
    @JSONField(serialize = false)
    public String getAdResource() {
        if (mAdResource == null) {
            if (isHaveRules()) {
                ArrayList<Bean> beans = body.rule.get(Const.POS_SPLASH);
                if (beans != null) {
                    mAdResource = new ArrayList<>();
                    for (Bean bean : beans) {
                        if (bean.isValid()) {
                            mAdResource.add(bean.source);
                        }
                    }
                }
            }
        }

        if (mAdResource != null && mAdResource.size() > 0) {
            String source = mAdResource.get(0);
            return TextUtils.isEmpty(source) ? "三方廣告" : source;
        } else {
            return "三方廣告";
        }
    }
}

錯誤原因:

這個mAdResource 在多個方法裏面都有賦值操作,如果在一個方法裏面正在遍歷,另一個方法裏面直接清楚了,那麼就會發現數組越界異常。
大家最好在自己方法裏面,單獨賦值給一個局部變量,然後對局部變量進行操作。或者加上鎖。

比如一下代碼,把ma 賦值給局部變量a,然後對a 進行操作,就不會有多線程問題。

Object a = ma;
a.setValue

錯誤堆棧:

java.lang.IllegalThreadStateException
	at java.lang.Thread.start(Thread.java:724)
	at com.zhangyue.ireader.zyadsdk.comm.managers.n.b(SourceFile:101)
	at com.zhangyue.ireader.zyadsdk.comm.managers.n.preLoadVideoMaterial(SourceFile:86)
	at com.zhangyue.iReader.module.driver.ad.a.loadAdStrategy(SourceFile:334)
	at com.zhangyue.iReader.module.proxy.AdProxy.loadAdStrategy(SourceFile:72)
	at com.zhangyue.iReader.module.idriver.ad.AdUtil.updateAdSchedule(SourceFile:33)
	at com.zhangyue.iReader.free.FreeModelReceiver.onReceive(SourceFile:41)
	at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(SourceFile:313)
	at android.support.v4.content.LocalBroadcastManager$1.handleMessage(SourceFile:121)
	at android.os.Handler.dispatchMessage(Handler.java:106)
	at android.os.Looper.loop(Looper.java:224)
	at android.app.ActivityThread.main(ActivityThread.java:7073)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:928)

有問題的:

 private void initHandler() {
        if (mHandlerThread == null) {
            mHandlerThread = new HandlerThread("LoadVideoMaterial", Process.THREAD_PRIORITY_BACKGROUND);
            mHandlerThread.start();
        }

修改之後:

    private synchronized void  initHandler() {
        if (mHandlerThread == null) {
            mHandlerThread = new HandlerThread("LoadVideoMaterial", Process.THREAD_PRIORITY_BACKGROUND);
            mHandlerThread.start();
        }

原因:

因爲這個方法可能會把多個線程調用,所以,只是判空不能解決多線程的問題。所以,保險起見,所有被多個線程調用的地方,都要做
多線程處理。尤其是線程啓動這種,只能調用一次的地方。

總結:

1. 變量在多個方法都有操作,容易多線程異常
2. 最好賦值給一個新的本地變量。
3. 線程開啓問題,線程啓動最好都有多線程處理。
發佈了431 篇原創文章 · 獲贊 57 · 訪問量 38萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章