更改存储在不同线程列表中的对象的字段
Changing field of an object that stored on a list that is on different thread
所以我有两个线程,线程 A 和 B。线程 A 包含一个包含我的对象的 ArrayList。我想要做的是:在线程 B 上,创建新对象并将其添加到该列表中。然后在线程 A 上更改该对象的字段。之后,在线程 B 上读取更改的字段。但遗憾的是,当我在线程 B 上读取它时,我得到了在对象构造函数上创建的默认值。那么,我如何读取新的值?
更新
一些信息:API 在应用程序启动时执行的 onEnable 方法我使用名为“Bukkit”,绑定到 Bukkit 的所有内容都在线程 A 上执行,线程 A 是 Bukkit 的主线程
Main.class:
private static ExecutorService executorService;
private static List<Confirmation> confirmations;
public void onEnable() {
executorService = Executors.newCachedThreadPool();
confirmations = new ArrayList<>();
}
public static ExecutorService getExecutorService() {
return executorService;
}
public static Confirmation createConfirmation(CommandSender sender, String command, String[] args) {
removeConfirmation(sender, command);
Confirmation confirmation = new Confirmation(sender, command, args);
confirmations.add(confirmation);
return confirmation;
}
public static void removeConfirmation(CommandSender commandSender, String command) {
confirmations.removeIf(confirmation -> confirmation.getSender().getName().equals(commandSender.getName()) && confirmation.getCommand().equalsIgnoreCase(command));
}
public static Confirmation getConfirmation(CommandSender commandSender, String command) {
return confirmations.stream().filter(confirmation -> confirmation.getSender().getName().equals(commandSender.getName()) && confirmation.getCommand().equalsIgnoreCase(command)).findFirst().orElse(null);
}
public static Confirmation createConfirmationIfNull(CommandSender commandSender, String command, String[] args) {
Confirmation confirmation = getConfirmation(commandSender, command);
return confirmation != null ? confirmation : createConfirmation(commandSender, command, args);
}
Confirmation.class:
private final CommandSender sender;
private final String command;
private final String[] args;
private boolean confirmed;
public Confirmation(CommandSender sender, String command, String[] args) {
this.sender = sender;
this.command = command;
this.args = args;
this.confirmed = false;
}
public CommandSender getSender() {
return sender;
}
public String getCommand() {
return command;
}
public String[] getArgs() {
return args;
}
public boolean isConfirmed() {
return confirmed;
}
public void setConfirmed(boolean bln) {
this.confirmed = bln;
}
Command.class:
@CommandHandler(name = "rank", usage = "/rank <set|prefix>", requiredRank = Rank.ADMINISTRATOR)
public void rank(CommandSender sender, Command command, String s, String[] args) { //Always called on Thread A
String str = args[0];
RankCommandAction action = Arrays.stream(RankCommandAction.values()).filter(rankCommandAction -> rankCommandAction.getName().equalsIgnoreCase(str)).findFirst().orElse(null);
if (action == null) {
sender.sendMessage(ChatColor.RED + "Usage: /rank <set|prefix>");
return;
}
Main.getExecutorService().submit(() -> action.getConsumer().accept(sender, args)); //Thread B
}
private enum RankCommandAction {
SET_PREFIX("prefix", (CommandSender sender, String[] args) -> {
CommandConfirmationEvent event = new CommandConfirmationEvent(sender, "rank", args, ChatColor.RED + "Are you sure?");
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
sender.sendMessage("Prefix changed");
}
}),
SET_RANK("set", (CommandSender sender, String[] args) -> {
CommandConfirmationEvent event = new CommandConfirmationEvent(sender, "rank", args, ChatColor.RED + "Are you sure?");
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
sender.sendMessage("Changed rank");
}
});
CommandConfirmationEvent.class:
private final CommandSender commandSender;
private final String command;
private final String[] args;
private final Confirmation confirmation;
private String warningMessage;
public CommandConfirmationEvent(CommandSender commandSender, String command, String[] args, String warningMessage) {
this.commandSender = commandSender;
this.command = command;
this.args = args;
this.warningMessage = warningMessage;
this.confirmation = Main.createConfirmationIfNull(commandSender, command, args);
}
public CommandSender getCommandSender() {
return commandSender;
}
public String getCommand() {
return command;
}
public String[] getArgs() {
return args;
}
public Confirmation getConfirmation() {
return confirmation;
}
public String getWarningMessage() {
return warningMessage;
}
public void setWarningMessage(String warningMessage) {
this.warningMessage = warningMessage;
}
@Override
public boolean isCancelled() {
return this.confirmation != null && !this.confirmation.isConfirmed();
}
这是调用事件时实际执行的内容:
if (!event.getConfirmation().isConfirmed()) {
event.getCommandSender().sendMessage("Please confirm");
return;
}
Main.removeConfirmation(event.getConfirmation());
最后,这是确认命令(在线程 A 上):
@CommandHandler(name = "confirm")
public void confirm(CommandSender sender, Command cmd, String str, String[] args) { //Always called on Thread A
Confirmation confirmation = findConfirmation(sender, "confirm", args);
if (confirmation != null) {
confirmation.setConfirmed(true);
String[] arg = confirmation.getArgs();
Bukkit.dispatchCommand(sender, confirmation.getCommand() + (arg != null && arg.length > 0 ? " " + StringUtils.merge(arg, " ") : "")); //This runs the "rank" command again
Main.removeConfirmation(confirmation);
}
}
private Confirmation findConfirmation(CommandSender sender, String[] args) {
List<Confirmation> list = Main.getConfirmations(sender);
if (list.isEmpty())
return null;
Confirmation confirmation;
if (list.size() > 1) {
if (args.length == 0) {
sender.sendMessage(ChatColor.RED + "Since you have multiple confirmations awaiting, you must specify the confirmation you want to confirm.");
sender.sendMessage(ChatColor.RED + "Usage: /confirm <command>");
sender.sendMessage(ChatColor.RED + "Awaiting confirmations: " + StringUtils.merge(list.stream().map(Confirmation::getCommand).toArray(String[]::new), ", ") + ".");
return null;
} else {
Confirmation foundConfirmation = list.stream().filter(confirmation1 -> confirmation1.getCommand().equalsIgnoreCase(args[0])).findFirst().orElse(null);
if (foundConfirmation == null) {
sender.sendMessage(ChatColor.RED + "You don't have any confirmations for the command '" + args[0] + "'.");
return null;
} else {
confirmation = foundConfirmation;
}
}
} else {
confirmation = list.get(0);
}
return confirmation;
}
您应该在两个线程上使用相同的对象实例,并在访问该对象时同步它们。
你的描述“Thread A contains an Arraylist”没有任何意义。如果它是局部变量,线程 B 永远无法访问它。
问题
- 根据给定的代码,它可能会在这一行抛出 IndexOutOfBounds 异常
objects.get(0).setName("TEST");
原因
- 提交给执行者服务的任务将运行在自己的时间轴上(除了休眠
1000
)
- 主线程休眠
200
无济于事(即使增加到 2000
也可能无济于事)
应该做什么
- 创建两个倒计时闩锁
- 将一个 latch 传递给提交的任务
- 设置后,会在latch上倒计时,等待其他latch
- main 将等待任务
倒计时的锁存器
- 主线程,一旦收到任务线程的通知,就会在其他latch上进行更新和倒计时
- 现在任务将收到通知并打印更新后的值
static class MyObject {
volatile String value;
MyObject(String value) {
this.value = value;
}
}
public static void main(String[] args) {
final List<MyObject> objects = new ArrayList<>();
ExecutorService executorService = Executors.newSingleThreadExecutor();
CountDownLatch updateLatch = new CountDownLatch(1);
CountDownLatch addLatch = new CountDownLatch(1);
executorService.submit(() -> {
objects.add(new MyObject("NAME"));
try {
addLatch.countDown();
updateLatch.await();
System.out.println(objects.get(0).value); //This prints "NAME"
} catch (Exception e) {
e.printStackTrace();
}
});
try {
addLatch.await();
objects.get(0).value = "TEST";
updateLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
我发现了问题:我在执行确认命令时从列表中删除了对象,它在 运行 再次执行“排名”命令之前删除了对象。这导致第二次“排名”命令执行找不到确认对象。
所以我有两个线程,线程 A 和 B。线程 A 包含一个包含我的对象的 ArrayList。我想要做的是:在线程 B 上,创建新对象并将其添加到该列表中。然后在线程 A 上更改该对象的字段。之后,在线程 B 上读取更改的字段。但遗憾的是,当我在线程 B 上读取它时,我得到了在对象构造函数上创建的默认值。那么,我如何读取新的值?
更新
一些信息:API 在应用程序启动时执行的 onEnable 方法我使用名为“Bukkit”,绑定到 Bukkit 的所有内容都在线程 A 上执行,线程 A 是 Bukkit 的主线程
Main.class:
private static ExecutorService executorService;
private static List<Confirmation> confirmations;
public void onEnable() {
executorService = Executors.newCachedThreadPool();
confirmations = new ArrayList<>();
}
public static ExecutorService getExecutorService() {
return executorService;
}
public static Confirmation createConfirmation(CommandSender sender, String command, String[] args) {
removeConfirmation(sender, command);
Confirmation confirmation = new Confirmation(sender, command, args);
confirmations.add(confirmation);
return confirmation;
}
public static void removeConfirmation(CommandSender commandSender, String command) {
confirmations.removeIf(confirmation -> confirmation.getSender().getName().equals(commandSender.getName()) && confirmation.getCommand().equalsIgnoreCase(command));
}
public static Confirmation getConfirmation(CommandSender commandSender, String command) {
return confirmations.stream().filter(confirmation -> confirmation.getSender().getName().equals(commandSender.getName()) && confirmation.getCommand().equalsIgnoreCase(command)).findFirst().orElse(null);
}
public static Confirmation createConfirmationIfNull(CommandSender commandSender, String command, String[] args) {
Confirmation confirmation = getConfirmation(commandSender, command);
return confirmation != null ? confirmation : createConfirmation(commandSender, command, args);
}
Confirmation.class:
private final CommandSender sender;
private final String command;
private final String[] args;
private boolean confirmed;
public Confirmation(CommandSender sender, String command, String[] args) {
this.sender = sender;
this.command = command;
this.args = args;
this.confirmed = false;
}
public CommandSender getSender() {
return sender;
}
public String getCommand() {
return command;
}
public String[] getArgs() {
return args;
}
public boolean isConfirmed() {
return confirmed;
}
public void setConfirmed(boolean bln) {
this.confirmed = bln;
}
Command.class:
@CommandHandler(name = "rank", usage = "/rank <set|prefix>", requiredRank = Rank.ADMINISTRATOR)
public void rank(CommandSender sender, Command command, String s, String[] args) { //Always called on Thread A
String str = args[0];
RankCommandAction action = Arrays.stream(RankCommandAction.values()).filter(rankCommandAction -> rankCommandAction.getName().equalsIgnoreCase(str)).findFirst().orElse(null);
if (action == null) {
sender.sendMessage(ChatColor.RED + "Usage: /rank <set|prefix>");
return;
}
Main.getExecutorService().submit(() -> action.getConsumer().accept(sender, args)); //Thread B
}
private enum RankCommandAction {
SET_PREFIX("prefix", (CommandSender sender, String[] args) -> {
CommandConfirmationEvent event = new CommandConfirmationEvent(sender, "rank", args, ChatColor.RED + "Are you sure?");
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
sender.sendMessage("Prefix changed");
}
}),
SET_RANK("set", (CommandSender sender, String[] args) -> {
CommandConfirmationEvent event = new CommandConfirmationEvent(sender, "rank", args, ChatColor.RED + "Are you sure?");
Bukkit.getPluginManager().callEvent(event);
if (!event.isCancelled()) {
sender.sendMessage("Changed rank");
}
});
CommandConfirmationEvent.class:
private final CommandSender commandSender;
private final String command;
private final String[] args;
private final Confirmation confirmation;
private String warningMessage;
public CommandConfirmationEvent(CommandSender commandSender, String command, String[] args, String warningMessage) {
this.commandSender = commandSender;
this.command = command;
this.args = args;
this.warningMessage = warningMessage;
this.confirmation = Main.createConfirmationIfNull(commandSender, command, args);
}
public CommandSender getCommandSender() {
return commandSender;
}
public String getCommand() {
return command;
}
public String[] getArgs() {
return args;
}
public Confirmation getConfirmation() {
return confirmation;
}
public String getWarningMessage() {
return warningMessage;
}
public void setWarningMessage(String warningMessage) {
this.warningMessage = warningMessage;
}
@Override
public boolean isCancelled() {
return this.confirmation != null && !this.confirmation.isConfirmed();
}
这是调用事件时实际执行的内容:
if (!event.getConfirmation().isConfirmed()) {
event.getCommandSender().sendMessage("Please confirm");
return;
}
Main.removeConfirmation(event.getConfirmation());
最后,这是确认命令(在线程 A 上):
@CommandHandler(name = "confirm")
public void confirm(CommandSender sender, Command cmd, String str, String[] args) { //Always called on Thread A
Confirmation confirmation = findConfirmation(sender, "confirm", args);
if (confirmation != null) {
confirmation.setConfirmed(true);
String[] arg = confirmation.getArgs();
Bukkit.dispatchCommand(sender, confirmation.getCommand() + (arg != null && arg.length > 0 ? " " + StringUtils.merge(arg, " ") : "")); //This runs the "rank" command again
Main.removeConfirmation(confirmation);
}
}
private Confirmation findConfirmation(CommandSender sender, String[] args) {
List<Confirmation> list = Main.getConfirmations(sender);
if (list.isEmpty())
return null;
Confirmation confirmation;
if (list.size() > 1) {
if (args.length == 0) {
sender.sendMessage(ChatColor.RED + "Since you have multiple confirmations awaiting, you must specify the confirmation you want to confirm.");
sender.sendMessage(ChatColor.RED + "Usage: /confirm <command>");
sender.sendMessage(ChatColor.RED + "Awaiting confirmations: " + StringUtils.merge(list.stream().map(Confirmation::getCommand).toArray(String[]::new), ", ") + ".");
return null;
} else {
Confirmation foundConfirmation = list.stream().filter(confirmation1 -> confirmation1.getCommand().equalsIgnoreCase(args[0])).findFirst().orElse(null);
if (foundConfirmation == null) {
sender.sendMessage(ChatColor.RED + "You don't have any confirmations for the command '" + args[0] + "'.");
return null;
} else {
confirmation = foundConfirmation;
}
}
} else {
confirmation = list.get(0);
}
return confirmation;
}
您应该在两个线程上使用相同的对象实例,并在访问该对象时同步它们。
你的描述“Thread A contains an Arraylist”没有任何意义。如果它是局部变量,线程 B 永远无法访问它。
问题
- 根据给定的代码,它可能会在这一行抛出 IndexOutOfBounds 异常
objects.get(0).setName("TEST");
原因
- 提交给执行者服务的任务将运行在自己的时间轴上(除了休眠
1000
) - 主线程休眠
200
无济于事(即使增加到2000
也可能无济于事)
应该做什么
- 创建两个倒计时闩锁
- 将一个 latch 传递给提交的任务
- 设置后,会在latch上倒计时,等待其他latch
- main 将等待任务 倒计时的锁存器
- 主线程,一旦收到任务线程的通知,就会在其他latch上进行更新和倒计时
- 现在任务将收到通知并打印更新后的值
static class MyObject {
volatile String value;
MyObject(String value) {
this.value = value;
}
}
public static void main(String[] args) {
final List<MyObject> objects = new ArrayList<>();
ExecutorService executorService = Executors.newSingleThreadExecutor();
CountDownLatch updateLatch = new CountDownLatch(1);
CountDownLatch addLatch = new CountDownLatch(1);
executorService.submit(() -> {
objects.add(new MyObject("NAME"));
try {
addLatch.countDown();
updateLatch.await();
System.out.println(objects.get(0).value); //This prints "NAME"
} catch (Exception e) {
e.printStackTrace();
}
});
try {
addLatch.await();
objects.get(0).value = "TEST";
updateLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
我发现了问题:我在执行确认命令时从列表中删除了对象,它在 运行 再次执行“排名”命令之前删除了对象。这导致第二次“排名”命令执行找不到确认对象。