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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章