First of all, I'm a complete newbie in Java. What I want to achieve is wait for async results before I return them.
MainActivity.java:
...
private SendTransport.Listener sendTransportListener = new SendTransport.Listener() {
#Override
public String onProduce(JSONObject data) {
String result = receiveResult(data);
return result;
}
};
private String receiveResult(JSONObject data) throws InterruptedException {
String result = "";
final CountDownLatch latch = new CountDownLatch(1);
socket.emit("receive-result", data, new Ack() {
#Override
public void call(Object... args) {
result = args[1];
latch.countDown();
}
});
latch.await();
return result;
}
...
In the code above, the socket.emit function never executes. What's wrong with my code? Or better to use some other approach here? If yes, then what?
Related
I've implemented a simply work queue that receives tasks from a number of different threads. I want these tasks to return a value to their source thread, but can't figure out how to do that.
I've considered using a future, but there's no way to explicitly set the future's value. I could use a property, but I don't believe those are thread safe.
Every task is an implementation of DBRequest. The actual content varies, but the result of all activities is a string.
An asynchronous thread creates a DBRequest and submits it to the queue. The queue runs the task, which produces a string. How do I get that string back to the thread that created the DBRequest, and how can I cause my creator thread to wait for the result?
public interface DBRequest {
String execute(VdtsSysDB vdtsSysDB, BoardLoad currentLoad);
}
public class DBQueue implements Runnable {
private static DBQueue dbQueue;
private LinkedBlockingQueue<DBRequest> queue = new LinkedBlockingQueue<>();
private VdtsSysDB vdtsSysDB = new VdtsSysDB();
private ReentrantLock lock = new ReentrantLock();
private static final Logger LOG = LoggerFactory.getLogger(DBQueue.class);
private boolean kill = false;
private BoardLoad currentLoad;
private ProgressController progressController;
public static DBQueue getInstance() {
if (dbQueue == null) synchronized (DBQueue.class) {
if (dbQueue == null)
dbQueue = new DBQueue();
}
return dbQueue;
}
private DBQueue() {
}
public ReentrantLock getLock() {
return lock;
}
#Override
public void run() {
LOG.info("Starting DBQueue loop. Kill {}.", kill);
while (!kill) {
DBRequest dbRequest = removeRequest();
if (dbRequest != null) {
lock.lock();
String result = dbRequest.execute(vdtsSysDB, currentLoad);
lock.unlock();
if (progressController != null) Platform.runLater(() ->
progressController.updateDisplay(currentLoad));
}
}
vdtsSysDB.getEntityManager().close();
}
public void addRequest(DBRequest dbRequest) {
try {
queue.add(dbRequest);
LOG.info("Added request.");
} catch (Exception e) {
LOG.error("Can't add element.", e);
}
}
private DBRequest removeRequest() {
DBRequest result = null;
try {
//result = queue.poll(10, TimeUnit.SECONDS);
result = queue.take();
} catch (Exception e) {
LOG.error("Exception.", e);
}
return result;
}
public void killDBQueue() {
kill = true;
LOG.info("Shutting down DBQueue.");
}
public static void start() {
Thread thread = new Thread(DBQueue.getInstance(), "DBQueue Thread");
thread.start();
LOG.info("Starting DBQueue.");
}
public BoardLoad getCurrentLoad() {
if (currentLoad == null)
currentLoad = BoardLoad.getLastOpenLoad(vdtsSysDB);
return currentLoad;
}
public void setCurrentLoad(BoardLoad proposedLoad) {
// We can only have one open load, and by definition, the current load is open. So close it.
if (this.currentLoad != null && !this.currentLoad.equals(proposedLoad)) {
currentLoad.close(vdtsSysDB);
if (proposedLoad != null) {
this.currentLoad = vdtsSysDB.getEntityManager().find(BoardLoad.class, proposedLoad.getId());
} else this.currentLoad = null;
}
}
public ProgressController getProgressController() {
return progressController;
}
public void setProgressController(ProgressController progressController) {
this.progressController = progressController;
}
}
EDIT: I'm using this queue to synchronize database access, reducing the need for locks and ensuring that requests are completed sequentially. I don't believe there is any other way to achieve this sort of asynchronous request -> synchronous request change.
But I'd love to have that belief changed.
You should add a reference to the submitting thread in your DBRequest interface and implement a setResult(String result) (or similar) method to receive the result.
You can implement a CountDownLatch waiting (or similar) on your submitting thread run() method to wait setting latch up when sending request to queue and down in setResult method.
If I'm not clear just let me know and I'll elaborate.
I am trying to receive serial data from Arduino and i want to store the value in a variable how can i do it ?
I tried the code below but it is not storing the value of string in the array element t[0]
or is there a way to store reading from input stream ?
final String[] t = new String[1];
t[0]="0";
final Handler handler = new Handler();
stopThread = false;
buffer = new byte[1024];
Thread thread = new Thread(new Runnable()
{
public void run()
{
while(!Thread.currentThread().isInterrupted() && !stopThread)
{
try
{
int byteCount = inputStream.available();
if(byteCount > 0)
{
byte[] rawBytes = new byte[byteCount];
inputStream.read(rawBytes);
final String string=new String(rawBytes,"UTF-8");
handler.post(new Runnable() {
public void run()
{
textView.append(string);
t[0]=string;
}
});
}
}
catch (IOException ex)
{
stopThread = true;
}
}
}
});
thread.start();
return t[0];
In addition to TMH's answer, if you want to manage threads yourself or suggested code seems too complicated for now, here's a simpler way of using CompletableFuture:
CompletableFuture<Object> completableFuture = new CompletableFuture<>();
new Thread(new Runnable() {
#Override
public void run() {
// computation, reading input streams, etc
Object result = new Object();
completableFuture.complete(result);
}
}).start();
// get() will wait until it's completed
Object resultFromThread = completableFuture.get();
// further processing...
Maybe better solution will be something like this:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class ResultFromThread {
public static void main(String... args) throws ExecutionException, InterruptedException {
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
return "something";
});
String result = cf.get();
}
}
Instead of 'return "something";' you just need to add anything you want to do.
Another solution is (with handling an exception):
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class ResultFromThread {
public static void main(String... args) throws ExecutionException, InterruptedException {
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
return "something";//may also throw an exception
}).handle((result, throwable) -> {
if(throwable != null) {
System.err.println(throwable);//do something with exception
}
return result;
});
String result = cf.get();
}
}
You are setting the value of t[0] inside a new Thread which will run asynchronously. So it is possible that return t[0]; execute before another thread set the value of t[0]. You can use Thread#join write the code as below.
thread.start();
thread.join();
return t[0];
When you call Thread#join the parent thread will wait to finish the Thread on which you have called the join method.
However, there are several mechanisms to do that like CountDownLatch and CyclicBarrier or Future but I think Thread#join is the easy and best suited for your use case.
I have a function in iOS app that uses dispatch_group to group multiple rest request:
static func fetchCommentsAndTheirReplies(articleId: String, failure: ((NSError)->Void)?, success: (comments: [[String: AnyObject]], replies: [[[String: AnyObject]]], userIds: Set<String>)->Void) {
var retComments = [[String: AnyObject]]()
var retReplies = [[[String: AnyObject]]]()
var retUserIds = Set<String>()
let queue = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)
Alamofire.request(.GET, API.baseUrl + API.article.listCreateComment, parameters: [API.article.articleId: articleId]).responseJSON {
response in
dispatch_async(queue) {
guard let comments = response.result.value as? [[String: AnyObject]] else {
failure?(Helper.error())
return
}
print(comments)
retComments = comments
let group = dispatch_group_create()
for (commentIndex, comment) in comments.enumerate() {
guard let id = comment["_id"] as? String else {continue}
let relevantUserIds = helperParseRelaventUserIdsFromEntity(comment)
for userId in relevantUserIds {
retUserIds.insert(userId)
}
retReplies.append([[String: AnyObject]]())
dispatch_group_enter(group)
Alamofire.request(.GET, API.baseUrl + API.article.listCreateReply, parameters: [API.article.commentId: id]).responseJSON {
response in
dispatch_async(queue) {
if let replies = response.result.value as? [[String: AnyObject]] {
for (_, reply) in replies.enumerate() {
let relevantUserIds = helperParseRelaventUserIdsFromEntity(reply)
for userId in relevantUserIds {
retUserIds.insert(userId)
}
}
retReplies[commentIndex] = replies
}
dispatch_group_leave(group)
}
}
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
success(comments: retComments, replies: retReplies, userIds: retUserIds)
}
}
}
As you can see from my code, I fetch all the comments under the same article, then fetch coresponding replies under each comment. After all requests are done, I invoke my success callback. This can be achieved using GCD's dispatch_group.
Now I am migrating the same functionality to android.
public static void fetchCommentsAndTheirReplies(Context context, String articleId, final StringBuffer outErrorMessage, final Runnable failure, final ArrayList<JSONObject> outComments, final ArrayList<ArrayList<JSONObject>> outReplies, final HashSet<String> outUserIds, final Runnable success) {
final RequestQueue queue = Volley.newRequestQueue(context);
HashMap<String, String> commentParams = new HashMap<>();
commentParams.put(API.article.articleId, articleId);
JsonArrayRequest commentRequest = new JsonArrayRequest(Request.Method.GET, API.baseUrl + API.article.listCreateComment, new JSONObject(commentParams), new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
for (int i = 0; i < response.length(); i++) {
JSONObject comment = response.getJSONObject(i);
outComments.add(comment);
outUserIds.addAll(helperParseRelaventUserIdsFromEntity(comment));
outReplies.add(new ArrayList<JSONObject>());
//TODO: DISPATCH_GROUP?
String id = comment.getString("_id");
HashMap<String, String> replyParams = new HashMap<>();
replyParams.put(API.article.commentId, id);
final int finalI = i;
JsonArrayRequest replyRequest = new JsonArrayRequest(Request.Method.GET, API.baseUrl + API.article.listCreateReply, new JSONObject(replyParams), new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
for (int j = 0; j < response.length(); j++) {
JSONObject reply = response.getJSONObject(j);
outUserIds.addAll(helperParseRelaventUserIdsFromEntity(reply));
outReplies.get(finalI).add(reply);
}
} catch (JSONException ex) {}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {}
});
queue.add(replyRequest);
}
success.run();
} catch (JSONException ex) {}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
outErrorMessage.append(error.getMessage());
failure.run();
}
});
queue.add(commentRequest);
}
Note that I am using success is executed right after I get all the comments, and before getting all the replies.
So how can I group them and delay the response?
I am working on the hairy implementation like
taskCount++;
if (taskCount == totalCount) {
success.run();
}
in reply block, but it seems very tedious.
You can simply do it with this class I made to mimic the iOS behavior. Call enter() and leave() the same way you did in iOS with dispatch_group_enter and dispatch_group_leave and call notify() just after the requests you want to group, just like dispatch_group_notify. It also uses runnable the same way iOS uses blocks :
public class DispatchGroup {
private int count = 0;
private Runnable runnable;
public DispatchGroup()
{
super();
count = 0;
}
public synchronized void enter(){
count++;
}
public synchronized void leave(){
count--;
notifyGroup();
}
public void notify(Runnable r) {
runnable = r;
notifyGroup();
}
private void notifyGroup(){
if (count <=0 && runnable!=null) {
runnable.run();
}
}
}
Hope it helps ;)
Here is the Kotlin version of Damien Praca's answer. This will allow you to use Kotlin lambdas like this.
val dispatchGroup = DispatchGroup()
dispatchGroup.enter()
// Some long running task
dispatchGroup.leave()
dispatchGroup.notify {
// Some code to run after all dispatch groups complete
}
class DispatchGroup {
private var count = 0
private var runnable: (() -> Unit)? = null
init {
count = 0
}
#Synchronized
fun enter() {
count++
}
#Synchronized
fun leave() {
count--
notifyGroup()
}
fun notify(r: () -> Unit) {
runnable = r
notifyGroup()
}
private fun notifyGroup() {
if (count <= 0 && runnable != null) {
runnable!!()
}
}
}
There is no direct analogue of dispatch_group in plain Java or Android. I can recommend a few rather sophisticated techniques to produce a really clean and elegant solution if you're ready to invest some extra time in it. It's not gonna be one or two lines of code, unfortunately.
Use RxJava with parallelization. RxJava provides a clean way to dispatch multiple tasks, but it works sequentially by default. See this article to make it execute tasks concurrently.
Although this is not exactly the intended usecase, you can try the ForkJoinPool to execute your group of tasks and recieve a single result afterwards.
You may use Threads and Thread.join() with Handlers as an option.
quote from:https://docs.oracle.com/javase/tutorial/essential/concurrency/join.html
The join method allows one thread to wait for the completion of
another. If t is a Thread object whose thread is currently executing,
t.join(); causes the current thread to pause execution until t's
thread terminates. Overloads of join allow the programmer to specify a
waiting period. However, as with sleep, join is dependent on the OS
for timing, so you should not assume that join will wait exactly as
long as you specify.
Like sleep, join responds to an interrupt by exiting with an
InterruptedException.
EDIT:
You should also check my event dispatcher gist. You may like it.
I use java.util.concurrent.CountDownLatch to achieve the goal.
First of all I made a interface for each task.
interface GroupTask {
void onProcessing(final CountDownLatch latch);
}
Then I create a class to handle grouping tasks.
interface MyDisptchGroupObserver {
void onAllGroupTaskFinish();
}
class MyDisptchGroup {
private static final int MSG_ALLTASKCOMPLETED = 300;
private CountDownLatch latch;
private MyDisptchGroupObserver observer;
private MsgHandler msgHandler;
private class MsgHandler extends Handler {
MsgHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
switch(msg.what) {
case MSG_ALLTASKCOMPLETED:
observer.onAllGroupTaskFinish();
break;
default:
break;
}
}
}
MyDisptchGroup(List<GroupTask> tasks, MyDisptchGroupObserver obj) {
latch = new CountDownLatch(tasks.size());
observer = obj;
msgHandler = new MsgHandler(getActivity().getMainLooper())
new Thread( new Runnable() {
#Override
public void run() {
try {
latch.await();
Log.d(TAG, "========= All Tasks Completed =========");
msgHandler.sendEmptyMessage(MSG_ALLTASKCOMPLETED);
} catch() {
e.printStackTrace();
}
}
}).start();
for( GroupTask task : tasks ) {
task.onProcessing(latch);
}
}
}
Of course I have more than one task implementation as the following.
The Task1
class Task1 implements GroupTask {
#Override
public void onProcessing(final CountDownLatch latch) {
new Thread( new Runnable() {
#Override
public void run() {
// Just implement my task1 stuff here
// The end of the Task1 remember to countDown
latch.countDown();
}
}).start();
}
}
And Task2
class Task2 implements GroupTask {
#Override
public void onProcessing(final CountDownLatch latch) {
new Thread( new Runnable() {
#Override
public void run() {
// Just implement my task2 stuff here
// The end of the Task2 remember to countDown
latch.countDown();
}
}).start();
}
}
Now everything are ready to fire.
ArrayList<GroupTask> allTasks = new ArrayList<GroupTask>();
allTasks.add(new Task1());
allTasks.add(new Task2());
new MyDisptchGroup(allTasks, this);
I have a worker threadpool set up that executes a bit of work which I want to log in a central place.
To be more precise, I've extended the Thread class into a worker class, which checks the status of a concurrent queue. If it's empty, then it waits. As elements are added by another thread, notify() wakes the workers. Once they've completed the task, they wait for the next element in the queue.
What's the best practice to have each of the threads report their status at the end of each of their tasks?
public class PoolWorker extends Thread {
public ConcurrentLinkedQueue<Device> q;
public PoolWorker(ConcurrentLinkedQueue<Device> q, String type){
this.q = q;
this.type = type;
}
#Override
public void run(){
while (true)
{
Device d = null;
try{
synchronized(q){
while(q.isEmpty())
{
q.wait(); // wait for a notify()
}
d = q.remove();
}
// do some work
// report status of work completed
}
}
Try to do something like this
ExecutorService exec = Executors.newFixedThreadPool(10);
Runnable runn = new Runnable()
{
#Override
public void run()
{
System.out.println("");
}
};
exec.execute(runn);
As mentioned best way is to use BlockingQueue. Below is the sample code:
public class PoolWorker extends Thread {
public ArrayBlockingQueue<String> q;
public String type;
public PoolWorker(ArrayBlockingQueue<String> q, String type) {
this.q = q;
this.type = type;
}
#Override
public void run() {
while(true){
String work = null;
try {
System.out.println("PoolWorker.run:waiting .............");
work = q.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("PoolWorker.run..work: " + work);
}
}
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue<String> pool = new ArrayBlockingQueue<String>(100);
PoolWorker worker = new PoolWorker(pool, "Something");
worker.start();
addWork(pool, "work1");
addWork(pool, "work2");
addWork(pool, "work3");
addWork(pool, "work4");
addWork(pool, "work5");
//Just give enough time to run
Thread.sleep(5000);
}
private static void addWork(ArrayBlockingQueue<String> pool, String work) throws InterruptedException {
System.out.println("PoolWorker.addWork: " + work);
pool.put(work);
}
}
There is nice sample code available in Java documentation as well:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html
I have a SwingWorker thread with an IOBound task which is totally locking up the interface while it runs. Swapping out the normal workload for a counter loop has the same result. The SwingWorker looks basically like this:
public class BackupWorker extends SwingWorker<String, String> {
private static String uname = null;
private static String pass = null;
private static String filename = null;
static String status = null;
BackupWorker (String uname, String pass, String filename) {
this.uname = uname;
this.pass = pass;
this.filename = filename;
}
#Override
protected String doInBackground() throws Exception {
BackupObject bak = newBackupObject(uname,pass,filename);
return "Done!";
}
}
The code that kicks it off lives in a class that extends JFrame:
public void actionPerformed(ActionEvent event) {
String cmd = event.getActionCommand();
if (BACKUP.equals(cmd)) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final StatusFrame statusFrame = new StatusFrame();
statusFrame.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
public void run () {
statusFrame.beginBackup(uname,pass,filename);
}
});
}
});
}
}
Here's the interesting part of StatusFrame:
public void beginBackup(final String uname, final String pass, final String filename) {
worker = new BackupWorker(uname, pass, filename);
worker.execute();
try {
System.out.println(worker.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
So far as I can see, everything "long-running" is handled by the worker, and everything that touches the GUI on the EDT. Have I tangled things up somewhere, or am I expecting too much of SwingWorker?
I think the problem is due to the call to SwingWorker.get() in your beginBackup method. Take a look at the docs for this method:
Waits if necessary for the computation
to complete, and then retrieves its
result.
This is a blocking call, hence your GUI becomes unresponsive.
(Also, is there any particular reason why you're doing an invokeLater from within an invokeLater call? You're already running on the EDT.)
Read the section from the Swing tutorial on Tasks That Have Interim Results for a working example. You will see that the get(...) method is invoked from within the process(...) method that is overridden in the SwingWorker class.