最近遇到的一个Java多线程问题
2013-08-03 10:54
393 查看
1. 问题描述
我的code出了一个多线程问题,错误如下:被同事指出问题出在多线程访问数据上,问题具体如下:项目中线程主要有两个:android自带的UIThread,GLSurfaceView中的GLThread负责渲染场景中所有的图形元素,两个线程同时访问对象内的数据。
代码原型如下:
class GLLinesGroupOverlay extends GLLinesOverlay { protected ArrayList<GLLinesOverlay> mLineGroup; // call in UIThread public void populate() { synchronized (mLineGroup) { // 初始化数据 } } // call in UIThread public boolean onTap(float x, float y) { synchronized (mLineGroup) { // 点击事件处理 } } // call in GLThread public void draw(GL10 gl) { synchronized (mLineGroup) { // 绘制 } } } class GLMultipleAAOverlay extends GLLinesGroupOverlay { @Override public void populate() { // ##### 操作mLineGroup,忘记加锁啦 ##### } }基类中对数据成员的操作都进行了加锁,写派生类的时候头脑不是特别清楚了,重写了方法但是忘记加锁,而且GLMultipleAAOverlay的构造和调用也很奇葩,代码如下:
// UI线程中调用构造overlay,添加到GL渲染队列中 GLMultipleAAOverlay mtOverlay = new GLMultipleAAOverlay(); addOverlay(mtOverlay); // 添加到GL渲染队列中 // ##### 时序问题 ##### mtOverlay.populate(); // 初始化数据。new了mtOverlay对象立刻添加到渲染队列中,addOverlay(mtOverlay)函数返回后 GLThread线程就不断调用该overlay的draw函数,然后UI线程再调用mtOverlay.populate()函数结果就悲催了,随机性crash,而且不同手机上行为不一致,在我的HTC G10上一直没crash过,在同事的手机上频率很高。
如果调用代码改成:
// UI线程中调用构造overlay,populate完成后添加到GL渲染队列中 GLMultipleAAOverlay mtOverlay = new GLMultipleAAOverlay(); mtOverlay.populate(); // 初始化数据。 addOverlay(mtOverlay); // 添加到GL渲染队列中这么调用populate完成后GL渲染才开始,不会有并发访问问题,然而确多了一个坑:如果GLMultipleAAOverlay重写onTap函数,而且忘记加锁就会有并发访问。
多线程本身是跟函数调用时序无关的,所以不能把希望季寄托于GLLinesGroupOverlay的所有函数被以某种正确的时序调用,多线程坑的根因出在派生类可以重写基类函数,任意访问数据成员mLineGroup所致。
2. 问题解决办法
想到了两种思路:方法1:mLineGroup私有化,取代数据protected的方式是一个final protected数据访问接口,而且接口内对数mLineGroup加锁。不过这么搞粒度过小了吧。
方法2:
class GLLinesGroupOverlay extends GLLinesOverlay { protected ArrayList<GLLinesOverlay> mLineGroup; // public final interface function // call in UIThread public final void populate() { synchronized (mLineGroup) { // 初始化数据 populateImpl(); } } // call in UIThread public final boolean onTap(float x, float y) { synchronized (mLineGroup) { onTapImpl(x, y); } } // call in GLThread public final void draw(GL10 gl) { synchronized (mLineGroup) { drawImpl(gl); } } // protected function implementation // ##### 这些函数可以被重写,但是不能加锁 ##### proteced void populateImpl() { // 操作数组,初始化数据 } protected boolean onTapImpl(float x, float y) { // 操作数组,点击事件处理 } protected boolean void drawImpl(GL10 gl) { // 操作数组,绘制 } } class GLMultipleAAOverlay extends GLLinesGroupOverlay { @Override protected void populateImpl() { // ~~~~~ 操作mLineGroup,不用加锁 ~~~~~ } }public final函数做架子,protected的重载函数做里子。架子中考虑了多线程访问问题,里子直接实现逻辑即可。
通过final阻止public接口被重写,protected函数实现接口可以重写,而且不需要也不能加锁(如果有人吃饱了没事干,还要加锁那可能也会有问题)!
数据被设成protected,希望派生来访问修改,而且希望派生类在加锁的情况下访问。 真心没有万全之策,只能寄希望于写派生类的人头脑清楚点!
3. final后语
Java 数据不想被修改,函数不想被重写 统一采用final。final类不能被继承,没有子类,final类中的方法默认是final的。
final方法不能被子类的方法覆盖,但可以被继承。
final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
final不能用于修饰构造方法。
C++ 数据不想被修改const;C++11才开始增加override和final关键字:http://www.devbean.net/2012/05/cpp11-override-final/
相关文章推荐
- 最近遇到一个很蛋疼的问题 关于View的
- debug和release编译结果在多线程的遇到的一个问题
- 说下最近遇到的一个引用相关的问题
- 在探索java i/o的Decorator模式时,遇到的一个问题.
- Java进度条多线程调用问题,一个很实用的问题
- 遇到问题---java--hibernate多线程中使用getCurrentSession报错innerSetException
- 最近遇到了一个问题,搞了好几天还是没有什么起色!郁闷中啦!
- 如何实现多个线程同步 (2013-11-10 12:07:24)转载▼ 标签: it 在编写一个类时,如果该类中的代码可能运行于多线程环境下,那么就要考虑同步的问题,Java实现线程同步的方法很多
- 关于java时间设置时遇到的一个问题。
- 经常遇到的一个问题是 MFC中开启多线程后 在非主线程中使用updata函数出现崩溃的情况。
- 多线程(6): Java Threads例子的一个问题
- 最近做了一个iOS小应用,记录下遇到的问题以及解决方法
- 初学Java遇到的一个数组小问题
- JAVA策略模式(3)之解决具体遇到的一个问题
- 最近编译qtopia4.4.3遇到的一个问题及解决方法
- 遇到问题---java--hibernate多线程中使用getCurrentSession报错innerSetException
- java -- 写时间的时候遇到一个问题,大家有没有遇到过
- 通过SVN导入两个项目(从java移植到andorid)时,一个android,一个普通java项目时可能遇到的两个问题
- 工作中遇到的一个多线程下导致RCW无法释放的问题
- java调用linux top命令的方法,以及遇到的一个问题