如何正确使用synchronized指令解决TextView不同步问题?
核心问题根源
你遇到的罗马音与片假名TextView不同步问题,本质是共享成员变量pickFromArray被多次修改导致的。当QandA方法被多次触发(比如SetQuestion在短时间内两次调用QandA,或按钮闪烁逻辑涉及修改该变量)时,这个共享变量的索引值会被覆盖,导致设置罗马音和片假名时使用了不同的索引,最终显示不匹配。
而你之前加的synchronized无效,是因为所有UI操作都在主线程执行,主线程是单串行模型,不存在多线程竞争场景,synchronized在这里无法起到预期的原子性保障作用。
解决方案
1. 将索引改为局部变量(最直接有效)
把pickFromArray改为QandA方法内的局部变量,让每个QandA调用使用独立的索引,彻底避免共享变量的干扰:
public void QandA(){ // 用局部变量存储随机索引,每个调用独立不冲突 int localPick = ThreadLocalRandom.current().nextInt(0, arraySize); textView29.setText(listromT.get(localPick)); hintView.setText(listT.get(localPick)); }
这种方式从根源上保证了罗马音和片假名始终使用同一个索引,完全无需依赖锁机制。
2. 移除冗余的runOnUiThread调用
你的SetQuestion Runnable已经通过主线程Handler.postDelayed执行,本身就在主线程运行,嵌套runOnUiThread属于冗余操作,可能导致任务调度混乱。修改后的代码:
public Runnable SetQuestion = new Runnable() { @Override public void run() { Qtimer++; // 合并条件判断,简化代码 if (Qtimer % 10 == 1 || Qtimer % 10 == 6) { QandA(); } if (Qtimer > 199) { Qtimer = 110; } // 直接将当前Runnable post到主线程队列 handler.postDelayed(this, 1000); } };
注意:确保你的handler是通过主线程Looper初始化的,比如handler = new Handler(Looper.getMainLooper()),保证任务在主线程执行。
3. 关于synchronized的正确使用场景
synchronized仅适用于多线程访问共享资源的场景,比如后台线程和主线程同时修改同一个数据。而你的所有UI操作都在主线程执行,主线程的代码是串行执行的,不存在并发冲突,因此不需要使用synchronized。如果按钮闪烁逻辑涉及后台线程操作共享数据,才需要针对共享资源(比如存储罗马音/片假名的列表)加锁,但你的场景中所有UI更新都应放在主线程完成,无需额外锁机制。
内容的提问来源于stack exchange,提问作者Binbows

