Cancel A Runnable (Java) - java

I am trying to make a repeating runnable only last a certain amount of times, but I can't find a method to cancel the repeating runnable once an integer reaches a certain number.
Bukkit.getScheduler().scheduleSyncRepeatingTask(main, new Runnable() {
public void run() {
Random rand = new Random();
int rnum = rand.nextInt(main.allowed.size()) + 1;
e.getPlayer().getInventory().addItem(main.allowed.get(rnum));
for(int i = 0; i >= main.getConfig().getInt("SpawnerCase.HowManySpawners"); i++) {
// Something here.
}
}
}, 0L, 0L);
Edit:
I just needed to know how to stop the runnable from inside that for statement. I got that idea from that link (How to stop a Runnable scheduled for repeated execution after a certain number of executions)

Please tell me if I am wrong but I think that you don't want to cancel the runnable inside the for loop.
That will stop the execution at that time, but I assume that it won't prevent it to be executed again and again because it is scheduled indefinitely. So my approach would be to unschedule it rather than terminate it inside the loop.
With this approach, I think you can do something like this, even tho it is a bit tricky:
//We use atomicInteger because the Runnable will be in other thread
AtomicInteger currentIteration = new AtomicInteger(0);
int maxAttempts = 100;
Map<String, Integer> idToProcessIdMap = new HashMap<>();
final String customProcessId = UUID.randomUUID().toString();
Consumer<String> endProcessConsumer = ((generatedId) -> {
int processId = idToProcessIdMap.get(generatedId);
Bukkit.getScheduler().cancelTask(processId);
});
int taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(main, new Runnable() {
public void run() {
Random rand = new Random();
int rnum = rand.nextInt(main.allowed.size()) + 1;
e.getPlayer().getInventory().addItem(main.allowed.get(rnum));
for(int i = 0; i >= main.getConfig().getInt("SpawnerCase.HowManySpawners"); i++) {
// Something here.
}
int currentIt = currentIteration.incrementAndGet();
if(currentIt > maxAttempts){
endProcessConsumer.accept(customProcessId);
}
}
}, 0L, 0L);
idToProcessIdMap.put(customProcessId, taskId);
Edit: Simplified version
AtomicInteger currentIteration = new AtomicInteger(0);
int maxAttempts = 100;
AtomicInteger processId = new AtomicInteger();
int taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(main, new Runnable() {
public void run() {
Random rand = new Random();
int rnum = rand.nextInt(main.allowed.size()) + 1;
e.getPlayer().getInventory().addItem(main.allowed.get(rnum));
for(int i = 0; i >= main.getConfig().getInt("SpawnerCase.HowManySpawners"); i++) {
// Something here.
}
int currentIt = currentIteration.incrementAndGet();
if(currentIt > maxAttempts){
Bukkit.getScheduler().cancelTask(processId.get());
}
}
}, 0L, 0L);
processId.set(taskId);
What I do in the code is first to create a variable to identify in which iteration we are.
Then I create a custom identifier for the process you are running and link it with the real process Id in a HashMap. We need to do this because when we run the process, we still don't know which is its id and therefore we won't be able to stop it directly
Also, I create a consumer which I can call inside the process when we reach the max execution times in order to unschedule itself.

Related

Assigning a bukkit runnable task to a Minecraft player using a hashmap

I have the following code.
I am trying to:
Assign a bukkit runnable task to a given ID
Assign a player a given ID
Place these two IDs into a hashmap, where each participant is matched to their respective bukkit runnable task
The repeating task should assign a maximum of 4 objects to a given player's inventory, assigning one object every minute.
This means that for each player, the repeating task should last a maximum of 4 minutes and should be cancelled when the counter exceeds the length of the hashmap.
However, I get the issue 'the local variable task may not have been initialised'.
I know that this means that I should initialise the variable 'task', but I am not sure how to do so, given that the variable task corresponds to the bukkit runnable task?
I would be so grateful for a helping hand!
Map<UUID, Integer> map = new HashMap<UUID, Integer>();
List<ItemStack> items = java.util.Arrays.asList(
new ItemStack(Material.WATER),
new ItemStack(Material.COBWEB),
new ItemStack(Material.CAKE),
new ItemStack(Material.RED_WOOL)
);
#EventHandler
public void on(PlayerQuitEvent event) {
map.remove(event.getPlayer());
}
#EventHandler
public void on(PlayerInteractEvent event) {
final ItemStack item = event.getItem();
if (item.getType() == Material.WHITE_WOOL) {
BukkitTask task = getServer().getScheduler().runTaskTimer(this, () -> {
if(this.stopRepeater) {
int counter = 0;
while (counter <= 4){
Material[] listofitems = {Material.WATER, Material.COBWEB, Material.CAKE, Material.SNOW};
int idx = counter;
Material randomItem = listofitems[idx];
ItemStack items = new ItemStack(randomItem);
Player thePlayer = event.getPlayer();
thePlayer.getInventory().addItem(items);
map.put(event.getPlayer().getUniqueId(),task.getTaskId());
counter ++;
if (counter >= map.size()) {
Bukkit.getServer().getScheduler().cancelTask(task.getTaskId());
}
}
}
}, 20 * 60, 20 * 60);
}
}
You might be able to get around this by splitting the BukkitTask task = ... line into BukkitTask task; and task = ..., though I've not tested it.
Thank you for your help!
I have come up with the following solution:
#EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
new BukkitRunnable() {
int count = 0;
Material[] listofitems = {Material.WATER_BUCKET, Material.CACTUS, Material.CAKE, Material.SNOW};
public void run() {
if (count == listofitems.length-1) cancel();
Material nextItem = listofitems[count];
ItemStack item = new ItemStack(nextItem);
event.getPlayer().getInventory().addItem(item);
count++;
}
}.runTaskTimer(this, 1200L, 1200L);
}
}
v

Java unexpected concurrent result

While testing concurrency, I found something unexpected.
Concurrency was controlled using concurrentHashMap and AtomicLong.
public class HumanRepository {
private final static Map<Long, Human> STORE = new ConcurrentHashMap<>();
private AtomicLong sequence = new AtomicLong();
public void save(Human human) {
STORE.put(sequence.incrementAndGet(), human);
}
public int size() {
return STORE.size();
}
public Long getSeq() {
return sequence.get();
}
}
I tested saving in multiple threads.
#Test
void name() throws NoSuchMethodException, InterruptedException {
final int threads = 3_500;
final ExecutorService es = Executors.newFixedThreadPool(threads);
final CountDownLatch count = new CountDownLatch(threads);
final HumanRepository repository = new HumanRepository();
for (int i = 0; i < threads; i++) {
try {
es.execute(() -> repository.save(new Human("aa")));
} finally {
count.countDown();
}
}
count.await();
System.out.println("seq = " + repository.getSeq());
System.out.println("size = " + repository.size());
}
I tested it with 3500 threads simultaneously. The result I expected is 3500 for both seq and size.
But sometimes I get seq=3499, size=3500.
That's weird. It is strange that seq does not come out as 3500, and even though the size is 3500, it does not make sense that seq is 3499.
I don't know why the data number and seq in the map are not the same and 3500 is not coming out.
** If you do Thread.sleep(400L); after count.await();, surprisingly, the value of seq is 3500
You are not actually waiting for all tasks to complete. Which means that if you get the 3500/3500 output, it's by chance.
Specifically, you decrease the countdown latch on the main thread after scheduling the job, instead of inside of the job, once it's done. That means your countdownlatch is basically just another glorified loop variable that doesn't do any inter-thread communication. Try something like this instead:
for (int i = 0; i < threads; i++) {
es.execute(() -> {
repository.save(new Human("aa"));
count.countDown();
});
}
You are calling count.countDown() outside the thread executing the HumanRepository.save(). So its possible that the main thread is not synchronized for the completion of the threads.
So you may see the results of repository.getSeq() while one thread is running. Can you try with the following code?
final int threads = 3_500;
final ExecutorService es = Executors.newFixedThreadPool(threads);
final CountDownLatch count = new CountDownLatch(threads);
final HumanRepository repository = new HumanRepository();
for (int i = 0; i < threads; i++) {
try {
es.execute(() -> {
repository.save(new Human("aa"));
count.countDown();
});
} finally {
}
}
count.await();
System.out.println("seq = " + repository.getSeq());
System.out.println("size = " + repository.size());

Keeping a counter with ExecutorService?

I'd like to keep a counter of executed threads, to use in the same threads that I am executing.
The problem here is that although the counter increases, it increases unevenly and from the console output I got this (I have a for loop that executes 5 threads with ExecutorService):
This is a test. N:3
This is a test. N:4
This is a test. N:4
This is a test. N:4
This is a test. N:4
As you can see instead of getting 1,2,3,4,5 I got 3,4,4,4,4.
I assume this is because the for loop is running fast enough to execute the threads, and the threads are fast enough to execute the code requesting for the counter faster than the counter can update itself (does that even make sense?).
Here is the code (it is smaller and there is no meaningful use for the counter):
for (int i = 0; i < 5; i++)
{
Thread thread;
thread = new Thread()
{
public void run()
{
System.out.println("This is test. N: "+aldo );
//In here there is much more stuff, saying it because it might slow down the execution (if that is the culprit?)
return;
}
};
threadList.add(thread);
}
//later
for (int i = 0; i < threadList.size(); i++)
{
executor.execute(threadList.get(i));
aldo = aldo + 1;
}
executor.shutdown();
try
{
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
}
catch (InterruptedException e)
{
}
Yes, aldo the counter ( with a few other lists, I think) are missing from the code (they are very simple).
The best way I know of doing this is by creating a custom thread class with a constructor that passes in a number. The variable holding the number can then be used later for any needed logging. Here is the code I came up with.
public static void main(String[] args) {
class NumberedThread implements Runnable {
private final int number;
public NumberedThread(int number) {
this.number = number;
}
#Override
public void run() {
System.out.println("This is test. N: " + number);
}
}
List<Thread> threadList = new ArrayList<>();
for (int i = 1; i < 6; i++) threadList.add(new Thread(new NumberedThread(i)));
ExecutorService executor = Executors.newFixedThreadPool(10);;
for (Thread thread : threadList) executor.execute(thread);
executor.shutdown();
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
}
catch (InterruptedException ignored) { }
}
You could also use a string object instead if you wanted to name the threads.
aldo is not modified by the tasks in the thread, but instead is modified in the main thread, here:
for (int i = 0; i < threadList.size(); i++) {
executor.execute(threadList.get(i));
//here...
aldo = aldo + 1;
}
Also, since you want a counter that can increase its value in several threads, then you may use an AtomicInteger rather than int.
Your code should look like this:
AtomicInteger aldo = new AtomicInteger(1);
for (int i = 0; i < 5; i++) {
executor.execute( () -> {
System.out.println("This is test. N: " + aldo.getAndIncrement());
});
}

Java - Instantiate Runnable Inside Loop

I want to create multiple Runnable object for alarm application which will execute tasks in the time specified by user.
I try to do it inside loop like the following:
ScheduledExecutorService wait;
List<Runnable> listens = new ArrayList<>();
int i;
private void playAlarmOnInit(){
wait = Executors.newScheduledThreadPool(3);
// loop through the tasks to get times
int counts = getDays().size();
for(i = 0; i < counts; i++){
if(!getDays().get(i).isEmpty()) {
Runnable listen = new Runnable() {
#Override
public void run() {
if(!getDays().get(i).equals("Everyday")) {
System.out.println(getDays().get(i) + " " + getTimes().get(i));
} else {
DateFormat format = new SimpleDateFormat("d-M-yyyy");
Date date = new Date();
String time = format.format(date);
System.out.println(time + " " + getTimes().get(i));
}
// System.out.println(" " + getTimes().get(i));
}
};
wait.scheduleAtFixedRate(listen, 0, 1, TimeUnit.SECONDS);
}
}
}
And it does nothing.
Why the code above does not work?
your problem is probably that you use i in your Runnable. At the time your Runnable is being executed, the value of i should be equal counts, so getDays().get(i) in the Runnuble should actually throw a IndexOutOfBoundException. Try to use try-catch and check, if there is an exception. To fix that you should create a new final variable and use it in the Runnable:
if(!getDays().get(i).isEmpty()) {
final int runnableI = i;
Runnable listen = new Runnable() {
#Override
public void run() {
if(!getDays().get(runnableI).equals("Everyday")) {
....
Or you could even store the day as the final variable:
final String day = getDays().get(i);
and use it in the Runnable
maybe the main program closing before threads are started. Add Thread.sleep(5000), after:
wait.scheduleAtFixedRate(listen, 0, 1, TimeUnit.SECONDS);
Thread.spleep(5000)
or maybe your list (getDays) is empty.

start multiple threads at the same time

For our assignment for class, we have to count the amount of words in a txt file by splitting it into n segments, which we are supposed to be able to set before launching the programm. Each segment should then get its own thread, which counts the words and then stops. At the end, the main thread should collect all the individual word counts and add them together.
This is (part of) what I wrote so far
for (int i = 0; i < segments; i++){
Thread thread = new Thread();
thread.start();
int words = counting(stringarray[i]);
totalwords += words;
long nanos = ManagementFactory.getThreadMXBean().getThreadCpuTime(Thread.currentThread().getId());
System.out.println("This Thread read " + words + " words. The total word count now is " + totalwords +
". The time it took to finish for this thread is " + nanos +".");
System.out.println("Number of active threads from the given thread: " + Thread.activeCount());
}
Now, while this gets the primary job done (counting the words in different threads and adding them to the total), I dont know how to just "leave the thread be" and then add the individual wordcounts together after every thread has done its job.
Additionally, while this is definitely starting multiple threads, it only ever prints out that I have 2, or maybe 3 threads running at a time, even if I split the txt into 100 segments. Is there a way to have them all run at the same time?
The wording of the question suggest that each thread has its own counter, so I would declare a thread class:
public class WordCounter extends Thread {
private String text;
private int count;
public WordCounter(String text) {
this.text = text;
}
public int getCount() {
return count;
}
#Override
public void run() {
count = counting(text);
}
}
and use it as follows:
WordCounter[] threads = new WordCounter[segments];
for (int i = 0; i < segments; ++i) {
threads[i] = new WordCounter(stringarray[i]);
threads[i].start();
}
int total = 0;
for (int i = 0; i < segments; ++i) {
threads[i].join();
total += threads[i].getCount();
}
You may use next code snippet as a basis.
Note, that in case you increment common variable in different threads, this operation has to be thread-safe. That's why AtomicInteger variable is used as a counter
final List<String> segments = new ArrayList<>();
//TODO:Fill segments ... this is up to you
//In case threads will increment same variable it has to be thread-safe
final AtomicInteger worldCount = new AtomicInteger();
//Create Thread for each segment (this is definitely not optimal)
List<Thread> workers = new ArrayList<>(segments.size());
for (int i = 0; i < segments.size(); i++) {
final String segment = segments.get(i);
Thread worker = new Thread(new Runnable() {
#Override
public void run() {
//increment worldCount
worldCount.addAndGet(counting(segment));
}
});
workers.add(worker);
worker.start();
}
//Wait until all Threads are finished
for (Thread worker : workers) {
worker.join();
}
int result = worldCount.get();
Same solutions, but with Executors:
final List<String> segments = new ArrayList<>();
segments.add("seg1");
segments.add("seg2");
segments.add("seg 3");
final AtomicInteger worldCount = new AtomicInteger();
List<Future> workers = new ArrayList<>(segments.size());
ExecutorService executor = Executors.newFixedThreadPool(segments.size());
for (String segment : segments) {
Future<Integer> worker = executor.submit(() -> worldCount.addAndGet(counting(segment)));
workers.add(worker);
}
executor.shutdown();
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
System.out.println("Still waiting...");
System.exit(0);
}
int result = worldCount.get();
System.out.println("result = " + result);

Categories