My current issue is that I need to be able to know the status of the basic player instance at all times. This would require a thread to get the status while the scanner waits for input from the user. The status would be used to automatically play the next audio file. Things I have done. First I made sure that this code that works inline. Next I tried to it in the thread, to no avail. I think it has to do with syncing the object between both threads
I tried making a synchronized getter of the basic player instance but it did not work
public class Player implements Runnable {
private String song;
private boolean comp = false;
private String path;
private final BasicPlayer player;
public Player () {
player = new BasicPlayer();
path = System.getProperty("user.dir") +"/src/Music/";
}
public void setSongName (String songName) {
song = songName;
}
public String getSongName() {
return song;
}
public void play () {
try {
player.open(new URL("file:///" + path + song));
player.play();
new Thread(new Player()).start();
} catch (BasicPlayerException | MalformedURLException e) {
e.printStackTrace();
}
}
private synchronized boolean isComplete () {
return getStatus() == 2;
}
public synchronized int getStatus(){
synchronized (player) {
return getPlayer().getStatus();
}
}
public synchronized boolean getComplete() {
return comp;
}
public synchronized void setComp(boolean comp) {
this.comp = comp;
}
private synchronized BasicPlayer getPlayer() {
synchronized (player) {
return player;
}
}
#Override
public void run() {
while (!isComplete()) {
BackendUtils.sleep(1000);
System.out.println(getStatus());
setComp(false);
}
setComp(true);
}
}
In this piece of code I am attempting to update a synchronized boolean to know then the audio is finished labeled comp. I try by using a synchronized player instance. The following code demonstrates how I tested if the threading was work or not.
import java.util.Scanner;
public class BackendTesting {
private static Scanner scanner = new Scanner(System.in);
private static Player player = new Player();
public static void main(String[] args) {
while (true) {
String input = input("$ ");
if (input.contains("ply play")) {
player.setSongName(input.substring(input.lastIndexOf(" ") + 1) + ".mp3");
player.play();
}
}
}
private static String input (String prompt) {
System.out.print(prompt);
return scanner.nextLine();
}
}
Try something like this:
In the thread class
public void run() {
MainClass.getInstance().playNextSong();
}
In the main class
public void playNextSong(){
synchronized(lock)
{
while (!isComplete()) {
lock.wait();
}
setComp(true);
lock.notifyAll();
}
}
Definitely you have to update it according to your needs.
Hope that helps.
Related
Trying to code a text game and when asking for GameSettings class input, the function gets called 3 times. I am trying to send the code back and forth between the classes, the reason why I am using different classes to make the code a bit more clean so that when I am sending the monsterHealth...etc it is readable.
Game.Java
package src;
import java.io.IOException;
public class Game {
public static void main(String[] args) throws IOException {
GameSettings GameSettings = new GameSettings();
GameSettings.init();
// GameSettings.Classes();
GameSettings.StartLogic();
if (src.GameSettings.Classes().equals("mage")) {
System.out.println("mage");
}
else if (src.GameSettings.Classes().equals("warrior")) {
System.out.println("warrior");
}
else if (src.GameSettings.Classes().equals("archer")) {
System.out.println("archer");
}
else {
System.out.println("Non valid");
}
}
}
GameSettings.Java
package src;
import java.util.Scanner;
public class GameSettings extends Game {
public interface classChoice {
}
public int playerHp;
private static Scanner scanner;
private static String nameInput;
private static String classChoice;
private String mage;
private String archer;
private String warrior;
public void init() {
scanner = new Scanner(System.in);
System.out.println("Welcome To Fizzle's Text Based RPG\nWhat Is Your
Name?");
nameInput = scanner.nextLine();
}
public static String Classes() {
System.out.println("Welcome " + nameInput + " What Class Would You Like
To Be?\n(mage)\n(warrior)\n(archer)");
classChoice = scanner.nextLine();
return classChoice;
}
public void StartLogic() {
playerHp = 10;
System.out.println(classChoice);
}
}
I see your problem. In the
GameSettings.StartLogic();
if (src.GameSettings.Classes().equals("mage")) {
System.out.println("mage");
}
else if (src.GameSettings.Classes().equals("warrior")) {
System.out.println("warrior");
}
else if (src.GameSettings.Classes().equals("archer")) {
System.out.println("archer");
}
else {
System.out.println("Non valid");
}
You're calling the GameSettings.Classes().equals() method three times. Instead of doing this, define a String variable before the if/else block like this:
GameSettings.StartLogic();
String input = src.GameSettings.Classes();
if (input.equals("mage")) {
System.out.println("mage");
}
else if (input.equals("warrior")) {
System.out.println("warrior");
}
else if (input.equals("archer")) {
System.out.println("archer");
}
else {
System.out.println("Non valid");
}
This is because when you use the if/else statement, you shouldn't call methods that rely on luck or user input inside of the statement, but define them as variables beforehand and pass those in as arguments to the if/else statement. Hope this helps!
Hello Fizzle! :)
Please clarify your question.
I added a few comments to your code:
Game.java
import java.io.IOException;
public class Game {
public static void main(String[] args) throws IOException {
GameSettings GameSettings = new GameSettings();
GameSettings.init();
GameSettings.StartLogic(); //returns null
if (GameSettings.Classes().equals("mage")) {
System.out.println("mage");
} else if (GameSettings.Classes().equals("warrior")) {
System.out.println("warrior");
} else if (GameSettings.Classes().equals("archer")) {
System.out.println("archer");
} else {
System.out.println("Non valid");
}
}
}
GameSettings.java
import java.util.Scanner;
public class GameSettings extends Game {
//why did you declare an Interface?
public interface classChoice {
}
public int playerHp;
private static Scanner scanner;
private static String nameInput;
private static String classChoice;
public void init() {
scanner = new Scanner(System.in);
System.out.println("Welcome To Fizzles Text Based RPG What Is Your Name?");
nameInput = scanner.nextLine();
}
public static String Classes() {
System.out.println("Welcome " + nameInput + " What Class Would You Like To Be?\n(mage)\n(warrior)\n(archer)");
classChoice = scanner.nextLine();
return classChoice;
}
//why are you calling this method beforehand?
public void StartLogic() {
playerHp = 10;
System.out.println("Your Class:" + classChoice);
}
}
DefaultRunners are producers
and OrderTaker is a consumer
They both share a OrderQueue.
Currently, I use the variable isDone to indicate if a game is finished.
Once each round is done, I want to make it repeat again and again.
However, in my current implementation it will only run once.
How could I solve it?
public class OrderQueue {
public synchronized void pushOrder(Order order) throws InterruptedException {
if (isDone) {
wait();
} else {
runnersQueue.addLast(order);
notifyAll();
}
}
public void pullOrder() {
try {
if (runnersQueue.size() == 0) {
} else if (isDone) {
wait();
} else {
handleOrder(runnersQueue.pop());
}
} catch (InterruptedException e) {
}
}
In my main class
while(true){
enterYesToStart();
DefaultRunners dfltRunner = new DefaultRunners(queue);
OrderTaker taker = new OrderTaker(queue);
taker.run();
System.out.println("This round is finished"); # never reach to this line
}
Here's the full source code for the example
https://gist.github.com/poc7667/d98e3bf5b3b470fcb51e00d9a0d80931
I've taken a look at your code snippets and the problem is fairly obvious.
The main thread runs the OrderTaker runnable. The main thread is stuck in an eternal loop as the while statement cannot complete unless it throws an exception. (Note that the same is true for your ThreadRunner runnable.)
This means that the main thread i still pulling orders while the race is already done.
The OrderTaker should exit it's while loop while once the race is done. I guess that there are multiple ways achieve this, but one way is use a shared variable.
I took your code and adapted it into a working example.
import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class RaceApp {
public static void main(String[] args) throws InterruptedException {
final RaceUpdateManager queue = new RaceUpdateManager();
for (int i = 0; i < 3; i++) {
queue.reset();
List<Thread> threads = Arrays.asList(
new Thread(new Runner("Tortoise", 0, 10, queue)),
new Thread(new Runner("Hare", 90, 100, queue))
);
for (Thread thread : threads) {
thread.start();
}
RaceUpdatesProcessor processor = new RaceUpdatesProcessor(queue);
processor.run();
System.out.println("Game finished");
}
}
private static class RaceUpdateManager {
private static final int TOTAL_DISTANCE = 300;
//thread-safe implementation for queue so no external syncrhonization is required when adding/removing updates
private final Deque<RaceUpdate> runnersQueue = new ConcurrentLinkedDeque<>();
//lock used to sync changes to runnersRecords and done variables
private final ReadWriteLock raceStatusLock = new ReentrantReadWriteLock();
private final Map<String, Integer> runnersRecords = new HashMap<>();
private volatile boolean raceDone = false;//volatile keyword guarantees visibility of changes to variables across threads
public boolean isRaceDone() {
return raceDone;
}
//updates can by added simultaneously (read lock)
public void register(RaceUpdate raceUpdate) throws InterruptedException {
Lock readLock = raceStatusLock.readLock();
readLock.lock();
try {
if (!raceDone) {
runnersQueue.addLast(raceUpdate);
}//ignore updates when the race is done
} finally {
readLock.unlock();
}
}
//but they need to be processed in order (exclusive write lock)
public void processOldestUpdate() {
Lock writeLock = raceStatusLock.writeLock();
writeLock.lock();
try {
RaceUpdate raceUpdate = runnersQueue.poll();
if (raceUpdate != null) {
handleUpdate(raceUpdate);
}
} finally {
writeLock.unlock();
}
}
private void handleUpdate(RaceUpdate raceUpdate) {
Integer distanceRun = runnersRecords.merge(
raceUpdate.runner, raceUpdate.distanceRunSinceLastUpdate, (total, increment) -> total + increment
);
System.out.printf("%s: %d\n", raceUpdate.runner, distanceRun);
if (distanceRun >= TOTAL_DISTANCE) {
raceDone = true;
System.out.printf("Winner %s\n", raceUpdate.runner);
}
}
public void reset() {
Lock writeLock = raceStatusLock.writeLock();
writeLock.lock();
try {
runnersQueue.clear();
runnersRecords.clear();
raceDone = false;
} finally {
writeLock.unlock();
}
}
}
public static class Runner implements Runnable {
private final String name;
private final int rest;
private final int speed;
private final RaceUpdateManager queue;
private final Random rand = new Random();
public Runner(String name, int rest, int speed, RaceUpdateManager queue) {
this.name = name;
this.rest = rest;
this.speed = speed;
this.queue = queue;
}
#Override
public void run() {
while (!queue.isRaceDone()) {
try {
if (!takeRest()) {
queue.register(new RaceUpdate(this.name, this.speed));
}
Thread.sleep(100);
} catch (InterruptedException e) {
//signal that thread was interrupted and exit method
Thread.currentThread().interrupt();
return;
}
}
}
private boolean takeRest() {
return rand.nextInt(100) < rest;
}
}
public static class RaceUpdatesProcessor implements Runnable {
private final RaceUpdateManager queue;
public RaceUpdatesProcessor(RaceUpdateManager queue) {
this.queue = queue;
}
#Override
public void run() {
while (!queue.isRaceDone()) {
try {
queue.processOldestUpdate();
Thread.sleep(50);
} catch (InterruptedException e) {
//signal that thread was interrupted and exit method
Thread.currentThread().interrupt();
return;
}
}
}
}
public static class RaceUpdate {
public final String runner;
public final int distanceRunSinceLastUpdate;
public RaceUpdate(String runner, int distanceRunSinceLastUpdate) {
this.runner = runner;
this.distanceRunSinceLastUpdate = distanceRunSinceLastUpdate;
}
}
}
I'm new to Java and is trying to learn the concept of livelock.
I found a great example of livelock online, where a husband and wife are trying to eat soup, but only have one spoon between them. Each spouse is too polite, and will pass the spoon if the other has not yet eaten.
My question is, what should we do to overcome the problem of livelock in general, and in this particular example? I am looking to modify my code to demonstrate the solution to the problem.
public class Livelock {
static class Spoon {
private Diner owner;
public Spoon(Diner d) { owner = d; }
public Diner getOwner() { return owner; }
public synchronized void setOwner(Diner d) { owner = d; }
public synchronized void use() {
System.out.printf("%s has eaten!", owner.name);
}
}
static class Diner {
private String name;
private boolean isHungry;
public Diner(String n) { name = n; isHungry = true; }
public String getName() { return name; }
public boolean isHungry() { return isHungry; }
public void eatWith(Spoon spoon, Diner spouse) {
while (isHungry) {
// Don't have the spoon, so wait patiently for spouse.
if (spoon.owner != this) {
try { Thread.sleep(1); }
catch(InterruptedException e) { continue; }
continue;
}
// If spouse is hungry, insist upon passing the spoon.
if (spouse.isHungry()) {
System.out.printf(
"%s: You eat first my darling %s!%n",
name, spouse.getName());
spoon.setOwner(spouse);
continue;
}
// Spouse wasn't hungry, so finally eat
spoon.use();
isHungry = false;
System.out.printf(
"%s: I am stuffed, my darling %s!%n",
name, spouse.getName());
spoon.setOwner(spouse);
}
}
}
public static void main(String[] args) {
final Diner husband = new Diner("Bob");
final Diner wife = new Diner("Alice");
final Spoon s = new Spoon(husband);
new Thread(new Runnable() {
public void run() { husband.eatWith(s, wife); }
}).start();
new Thread(new Runnable() {
public void run() { wife.eatWith(s, husband); }
}).start();
}
}
In general there isn't a universal solution to livelock.
A thread has to stop repeating the same actions if no progress has been detected.
In your example allowing spouse to eat more than once (thereby detecting that the loved one has not eaten and no progress has been made, since eating takes just 1 step) should force the owner of the spoon to just eat.
Obviously real life scenarios will be more elaborate, but detecting zero progress and acting differently than normally is crucial.
I've received a task about java.util.concurrent package of java. I've made it almost totally, but there is some bug or mistake. When the queue is empty and operator waits for 5 seconds method poll should retrieve null and pass it to the operator and operator goes home. But it doesn't happen. It retrieves null but doesn't pass it to the operator. Sorry for my English.)
public class Client extends Thread {
public CountDownLatch latch=new CountDownLatch(1);
private boolean waiting;
private final Random random=new Random();
public boolean isWaiting() {
return waiting;
}
public void setWaiting(boolean isWaiting) {
this.waiting = isWaiting;
}
private static final Logger LOGGER;
static {
LOGGER = Logger.getLogger(Client.class);
new DOMConfigurator().doConfigure("log4j.xml",
LogManager.getLoggerRepository());
LOGGER.setLevel(Level.INFO);
}
private int limitTime=new Random().nextInt(5000);
public void run(){
ClientQueue.enqueueClient(this);
while(waiting){
if (random.nextBoolean()){
try {
latch.await(5, TimeUnit.SECONDS);
if (!waiting) return;
ClientQueue.removeFromQueue(this);
reportTiredToWait();
sleep(random.nextInt(1000)+500);
ClientQueue.enqueueClient(this);
reportDecidedToCallAgain();
} catch (InterruptedException e) {
LOGGER.info("Exception");
}
}
}
}
public Client(String name) {
super(name);
this.waiting=true;
}
private void reportTiredToWait(){
LOGGER.info("Client "+getName()+" was tired to wait and decided to hang up");
}
private void reportDecidedToCallAgain(){
LOGGER.info("Client "+getName()+" decided to call again");
}
#Override
public String toString() {
return "Client "+getName();
}
}
public class ClientQueue {
private static final Logger LOGGER;
static {
LOGGER = Logger.getLogger(ClientQueue.class);
new DOMConfigurator().doConfigure("log4j.xml",
LogManager.getLoggerRepository());
LOGGER.setLevel(Level.INFO);
}
private static ClientQueue instance;
private BlockingQueue<Client> queue;
public static void printQueue(){
System.out.println("LIST OF CLIENTS:");
for (Client client :ClientQueue.getInstance().queue){
System.out.println("CLIENT "+client.getName());
}
System.out.println("END OF LIST OF CLIENTS:");
}
private static ClientQueue getInstance()
{
if ( instance == null )
{
instance = new ClientQueue();
}
return instance;
}
private ClientQueue()
{
this.queue = new LinkedBlockingQueue<Client>();
}
public static void enqueueClient(Client client){
getInstance().queue.add(client);
reportClientEnqueued(client.getName());
}
public static void removeFromQueue(Client client){
ClientQueue.getInstance().queue.remove(client);
reportClientDeletedFromQueue(client.getName());
}
public static Client pollFirst(long time, TimeUnit timeUnit) throws InterruptedException{
Client client=null;
client = getInstance().queue.poll(time, timeUnit);
if (client!=null){
reportClientRetrievedFromQueue(client.getName());
}
return client;
}
private static void reportClientEnqueued(String name){
LOGGER.info("Client "+name+" was put on the waiting list");
}
private static void reportClientDeletedFromQueue(String name){
LOGGER.info("Client " +name+" was deleted from waiting list");
}
private static void reportClientRetrievedFromQueue(String name){
LOGGER.info("Client " +name+" was retrieved from waiting list");
}
}
public class Operator extends Thread{
private static final Logger LOGGER;
static {
LOGGER = Logger.getLogger(Operator.class);
new DOMConfigurator().doConfigure("log4j.xml",
LogManager.getLoggerRepository());
LOGGER.setLevel(Level.INFO);
}
private boolean running;
public Operator(String name){
super(name);
running= true;
}
#Override
public void run() {
while (running){
Client client=null;
try {
client = ClientQueue.pollFirst(5, TimeUnit.SECONDS);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
if (client!=null){
String clientName=client.getName();
reportOperatorReceivedCall(clientName);
try {
client.setWaiting(false);
client.latch.countDown();
sleep(10000);
reportOperatorFinishedConversation(clientName);
} catch (InterruptedException e) {
LOGGER.error(e);
}
} else{
reportOperatorFinishedHisWork();
running=false;
}
}
}
private void reportOperatorReceivedCall(String name){
LOGGER.info("Operator "+getName()+" received call from Client "+name);
}
private void reportOperatorFinishedConversation(String name){
LOGGER.info("Operator "+getName()+" finished conversation with Client "+name);
}
private void reportOperatorFinishedHisWork(){
LOGGER.info("Operator "+getName()+" finished his work for today, he is too tired and decided to go home.");
}
}
public class Main {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
LinkedList<Client> clientList = new LinkedList<Client>();
clientList.add(new Client("Vasya"));
clientList.add(new Client("Tanya"));
clientList.add(new Client("Petya"));
clientList.add(new Client("Kolya"));
clientList.add(new Client("Elena"));
clientList.add(new Client("Anna"));
for(int i = 0; i < clientList.size(); i++) {
executor.schedule(clientList.get(i), i, TimeUnit.SECONDS);
}
LinkedList<Operator> operatorList = new LinkedList<Operator>();
operatorList.add(new Operator("Bob"));
operatorList.add(new Operator("Sandra"));
operatorList.add(new Operator("John"));
for(int i = 0; i < operatorList.size(); i++) {
executor.schedule(operatorList.get(i), 500, TimeUnit.MILLISECONDS);
}
}
}
You have an extra semicolon in ClientQueue.pollFirst. Here it is corrected:
public static Client pollFirst(long time, TimeUnit timeUnit) throws InterruptedException{
Client client=null;
client = getInstance().queue.poll(time, timeUnit);
if (client!=null) { // removed semicolon from this line
reportClientRetrievedFromQueue(client.getName());
}
return client;
}
thanks for all that helped me! I will be more detailed now. What I want to do is a Bukkit plugin which after one minute, and if the player dropped something, the console displays an information message, like "Players are moving", but I just can make the first message appear: "Player dropped something" and I think that the error is on the boolean that I used. Please, can anyone help me with bukkit? This is my code:
public class HgCake extends JavaPlugin implements Listener{
boolean reference = false;
#Override
public void onEnable() {
Bukkit.getServer().getPluginManager().registerEvents(this, this);
}
#EventHandler
public void onDropItem (PlayerDropItemEvent e) {
getLogger().info("Player dropped something");
reference = true;
}
public void onPlayerMove (PlayerMoveEvent e){
if (reference = true){
getLogger().info("Players are moving");
}
}
}
Bukkit has a built in scheduling system that you can read up on
Scheduler Programming
Use this instead of normal Java timers, trust me. It'll make your life easier in the long run.
To do what you're wanting to do, you'd need a BukkitRunnable class to give to the scheduler.
This is a generic one I over-simplified for example purposes:
public class Callback extends BukkitRunnable{
private Object targetObject;
public Method targetMethod;
private Object[] perameters;
public Callback(Object targetObject, String methodName, Object[] argsOrNull){
try {
this.targetMethod = targetObject.getClass().getMethod(methodName, (Class<?>[]) argsOrNull);
} catch (Exception e){
e.printStackTrace();
}
this.targetObject = targetObject;
this.perameters = argsOrNull;
}
public void run(){
try {
this.targetMethod.invoke(this.targetObject,perameters);
} catch (Exception e){
e.printStackTrace();
}
}
}
Then you create an object of that runnable, providing the callback method/props as args, and give it to the scheduler to run in 60 seconds:
For the movement part, you just watch that while the item is dropped and nobody's moved yet.
public class DropWatcher implements Listener {
private Boolean hasAnythingMoved;
private Boolean dropped;
private Pwncraft plugin;
private Player player;
public DropWatcher(Pwncraft plugin, Player player){
this.player = player;
this.hasAnythingMoved = false;
this.dropped = false;
this.plugin = plugin;
this.plugin.pluginManager.registerEvents(this, plugin);
}
//Drop event listener: When the player drops an item, it sets dropped to true, and initiates the countdown.
#EventHandler
public void onDropItem (PlayerDropItemEvent e) {
if(e.getPlayer().equals(this.player) && !this.dropped){
this.dropped = true;
BukkitCallbackTask doInSixtySeconds = new BukkitCallbackTask(this, "timesUp" , null);
doInSixtySeconds.runTaskLater(plugin, 1200); // time is in ticks (20 ticks +/- = 1 sec), so 1200 ticks = 1 min.
}
}
//Watches for other-players' movement, and sets hasAnythingMoved to true if so.
#EventHandler
public void onMove (PlayerMoveEvent e){
if(!e.getPlayer().equals(this.player) && this.dropped && !this.hasAnythingMoved){
this.hasAnythingMoved = true;
}
}
/*
This is the method the runnable calls when the timer is up.
It checks for movement, and if so, sends a message and explodes the player
(Just because it can. You're welcome to veto the explosion.)
*/
public void timesUp(){
if(this.hasAnythingMoved){
this.player.sendMessage("Someone moved! Time to party!");
this.player.getWorld().createExplosion(this.player.getLocation(), 5F);
this.dropped = false;
this.hasAnythingMoved = false;
}
}
}
You forgot the #EventHandler for the PlayerMoveEvent I think.
It should be:
public class HgCake extends JavaPlugin implements Listener{
boolean reference = false;
#Override
public void onEnable() {
Bukkit.getServer().getPluginManager().registerEvents(this, this);
}
#EventHandler
public void onDropItem (PlayerDropItemEvent e) {
getLogger().info("Player dropped something");
reference = true;
}
#EventHandler
public void onPlayerMove (PlayerMoveEvent e){
if (reference = true){
getLogger().info("Players are moving");
}
}
}
int plannedActivity = getServer().getScheduler().runTaskLaterAsynchronously(this, new Runnable() {
public void run() {
//whatever you want to do
}
}, 120L);