Java中Arrays.fill()提前执行致AsyncTask获全0密码数组问题
问题分析与解决方案
这个问题的核心是线程执行顺序的竞态条件:你在主线程中调用AsyncTask.execute()时,只是把后台任务提交到了线程池的任务队列,主线程并不会等待后台任务开始执行就继续往下运行Arrays.fill()。这就导致后台线程还没来得及读取密码数组,主线程就已经把数组填充为0了,所以BackGroundTask拿到的自然是全0数组。
用Handler.postDelayed()虽然能临时解决,但延迟时间是拍脑袋决定的——在性能差的设备上可能延迟不够,在性能好的设备上又会做无意义的等待,不是可靠的方案。
最优解决方案:让后台任务自己负责清理密码数组
最可靠且高效的方式是把密码数组的清理逻辑放到BackGroundTask内部,确保只有在后台任务使用完密码之后才执行清理。这样完全避免了线程间的竞态问题,逻辑也更清晰。
步骤1:修改BackGroundTask类
public class BackGroundTask extends AsyncTask<Void, Void, Boolean> { private final String username; private final char[] password; public BackGroundTask(String username, char[] password) { this.username = username; this.password = password; } @Override protected Boolean doInBackground(Void... params) { // 在这里执行你的业务逻辑:比如用username和password发起登录请求 boolean loginSuccess = performLoginRequest(username, password); // 业务逻辑执行完毕后,立即清理密码数组 Arrays.fill(password, '0'); return loginSuccess; } @Override protected void onPostExecute(Boolean success) { // 在这里处理任务完成后的UI更新,比如提示登录结果 super.onPostExecute(success); } // 示例:你的业务方法 private boolean performLoginRequest(String username, char[] password) { // 模拟网络请求或其他操作 // ... return true; } }
步骤2:简化主线程的按钮点击逻辑
String username = edtLabel.getText().toString(); char[] password = new char[edtPassword.getText().length()]; edtPassword.getText().getChars(0, edtPassword.getText().length(), password, 0); new BackGroundTask(username, password).execute(); // 这里不再需要手动调用Arrays.fill()!
为什么这个方案更好?
- 完全避免竞态条件:只有当后台任务确实使用完密码后,才会执行清理操作,不会出现提前清理的问题。
- 性能更优:不需要额外的线程延迟或数组复制操作,没有不必要的内存或时间开销。
- 职责更清晰:谁使用敏感数据,谁负责清理,符合代码设计的单一职责原则。
备选方案:复制密码数组(适合特殊场景)
如果你因为某些原因不能修改BackGroundTask的代码,可以在主线程中复制一份密码数组给后台任务,然后立即清理原数组:
String username = edtLabel.getText().toString(); char[] password = new char[edtPassword.getText().length()]; edtPassword.getText().getChars(0, edtPassword.getText().length(), password, 0); // 复制一份密码数组给后台任务 char[] taskPassword = Arrays.copyOf(password, password.length); new BackGroundTask(username, taskPassword).execute(); // 立即清理原数组 Arrays.fill(password, '0');
不过这个方案会额外占用一份数组的内存,不如第一种方案高效,只推荐在无法修改BackGroundTask时使用。
内容的提问来源于stack exchange,提问作者Skylark
相关产品推荐
相关产品推荐

