嘿,所有我试图添加到map<String, String>
的列表使用下面的代码:
@NonNull
public static List<Map<String, String>> mysqlSelect(@NonNull String select, String where) {
Map<String, String> info = new HashMap<String,String>();
List<Map<String, String>> list = new ArrayList<>();
int selectCnt = select.replaceAll("[^,]","").length();
final boolean[] b = {true};
new Thread(new Runnable() {
@Override
public void run(){
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
String sql = "SELECT fb_firstname, fb_lastname, fb_id FROM fbuser";
PreparedStatement statement = connection.prepareStatement(sql);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
info.put("name", resultSet.getString("fb_firstname"));
info.put("address", resultSet.getString("fb_lastname"));
info.put("phone_number", resultSet.getString("fb_id"));
list.add(info);
}
b[0] = false;
} catch (Exception e) {
Log.e("InfoAsyncTask", "Error reading school information", e);
b[0] = false;
}
}
}).start();
while (b[0]) { }
return list; //result.get("name")
}
当它在while () {..
部分循环时,它用所需的3个字符串值填充列表。然而,当它一次又一次地循环时,它似乎把所有以前的值填充到最后一次读取的值,而不是每次都有新的数据。
示例:
第1个循环:
name: bob barker
address: something
phone-number = 4235421212
第2个循环:
name: steve jobs
address: something again
phone-number = 8546521111
此时,第一循环数据变为
第1个循环:
name: steve jobs
address: something again
phone-number = 8546521111
第2个循环:
name: steve jobs
address: something again
phone-number = 8546521111
我错过了什么?
更新1
原来我有以下几点:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new InfoAsyncTask().execute();
}
@SuppressLint("StaticFieldLeak")
public class InfoAsyncTask extends AsyncTask<Void, Void, Map<String, String>> {
@Override
protected Map<String, String> doInBackground(Void... voids) {
Map<String, String> info = new HashMap<>();
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
String sql = "SELECT fb_firstname, fb_lastname, fb_id FROM fbuser LIMIT 1";
PreparedStatement statement = connection.prepareStatement(sql);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
info.put("name", resultSet.getString("fb_firstname"));
info.put("address", resultSet.getString("fb_lastname"));
info.put("phone_number", resultSet.getString("fb_id"));
}
} catch (Exception e) {
Log.e("InfoAsyncTask", "Error reading school information", e);
}
return info;
}
@Override
protected void onPostExecute(Map<String, String> result) {
if (!result.isEmpty()) {
TextView textViewName = findViewById(R.id.textViewName);
TextView textViewAddress = findViewById(R.id.textViewAddress);
TextView textViewPhoneNumber = findViewById(R.id.textViewPhone);
textViewName.setText(result.get("name"));
textViewAddress.setText(result.get("address"));
textViewPhoneNumber.setText(result.get("phone_number"));
}
}
}
但是我想把它放到它自己的类文件中,然后从onCreate调用它。
2条答案
按热度按时间svmlkihl1#
您遇到了并发问题-当您开始使用线程并更新其他线程可以看到的值时,无法保证一个线程看到的是另一个线程所做的,除非您使用某种形式的 * 同步 *。
(基本上,为了让电脑运行真实的快,他们会对工作的顺序和记录工作的位置进行一些自由。这就像在办公室的白板上更新当前数据一样,只是为了更快地工作,人们有时只是在办公桌上的记事本上写下事情,然后 * 稍后 * 再更新白板)
有很多方法可以解决这个问题--您可以在section on concurrency in the Java documentation中获得概述,并且有很多工具可供您使用。我强烈建议您阅读它(或者类似 *Java并发实践 * 的内容),因为这是一个非常重要的概念,可以帮助您了解--如果您不知道自己在使用线程做什么,可能会发生很多奇怪和糟糕的事情!
但在这种情况下,如果您有一个
boolean
,只有一个线程在向其写入数据,其他所有线程都在从其阅读数据,您可以将boolean
标记为volatile
。这意味着它会在所有位置更新这样就可以解决你的“为什么它仍然是真的”问题。(尽管您可能需要将其添加为Runnable
上的一个字段,并存储对它的引用,因为所有这些操作都是在函数中完成的)您在Map中写入的实际数据是更大的问题-您需要确保这些数据是同步的。(在所有线程上),但是Java也提供了一些并发意识的集合类,比如
ConcurrentHashMap
,它们会为你处理这些问题。就像竞争条件一样--同样,你必须了解围绕这些东西的问题,这样你才能注意避免它们p4tfgftt2#
我敢打赌,您已经想出了一个
boolean[]
来替代一个简单的boolean
标志,您不能从lambda表达式中更改它,因为它应该是 effectively final。这个技巧将允许更新标志,但由于它可能被一个新线程缓存,另一个线程可能无法观察到它已经被更新。要建立所谓的 happens-before 关系,需要使用
AtomicBoolean
而不是单元素boolean
数组b
,这样可以保证代码的行为是一致的。此外,
HashMap
不适用于多线程环境。解决更新后的问题
如果我没有理解错的话,你正在尝试重新实现使用过时的
AsyncTask
类开发的代码。由于之前您有一个异步生成
Map
并返回整个内容的代码,因此您实现了Callable
接口(与Runnable
相反,它能够生成返回值)以实现相同的结果。这就是这样的实现可能看起来的样子:
为了在单独的线程中运行
Callable
,可以使用ExcecutorService
的示例,它基本上是一种线程管理机制,它维护一个线程池或单个线程,并安排所提供任务的执行(可以同时提交Runnable
和Callable
)。当任务提交后,执行程序返回一个
Future
的示例,你可以把它想象成一个容器,里面装着将来某个时候的预期结果。若要取得
ExcecutorService
,您可以使用Executors
类别所提供的其中一个静态作坊方法。下面是一个使用单线程执行器的简单示例:
您也可以执行检查
isDone()
,以找出Future
实体是否包含结果: