Using Java stream forEach() in ScheduledExecutorService freezes - java

The general idea is to have a Runnable running every 10 seconds in background to check some data and if needed make changes in an object. ScheduledExecutorService is instantiated in method main() and the task is scheduled. Runnable task instantiates Crawler object and starts crawling. Most of the times it runs couple of times with success but when application is running and data changes one of crawler's method is fired but never ends. There is no loop in the code. I was trying to debug also without success. Maybe you will be able to spot where the problem lays.
Main:
public class Main {
public static void main(String[] args) {
DataStock dataStock = DataStock.getInstance();
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
ses.scheduleAtFixedRate(new EveryFiveSeconds(), 5, 5, TimeUnit.SECONDS);
// below the task which fails after couple of runs
ses.scheduleAtFixedRate(new EveryTenSeconds(), 1 , 10, TimeUnit.SECONDS);
dataStock.init();
Menu currentScreen = new UserMenu();
while(currentScreen != null) {
currentScreen = currentScreen.display();
}
}
}
EveryTenSeconds Runnable:
public class EveryTenSeconds implements Runnable {
#Override
public void run() {
Crawler crawler = new Crawler();
crawler.crawl();
}
}
Crawler:
public class Crawler {
private final DataStock dataStock;
public Crawler() {
this.dataStock = DataStock.getInstance();
}
public void crawl() {
checkOutRentables(dataStock.getCarServicesWithOwners().keySet());
checkFinancialBook(dataStock.getPaymentsBook(), dataStock.getCurrentDate());
}
private void checkOutRentables(Set<CarService> carServices) {
System.out.println("Start check...");
carServices.stream()
.flatMap(service -> service.getWarehousesSet().stream())
.filter(rentable -> !rentable.isAvailableForRent())
.forEach(RentableArea::refreshCurrentState);
System.out.println("Checking finished");
}
private void checkFinancialBook(Set<BookEntry> bookEntries, LocalDate currentDate) {
System.out.println("Start second check...");
bookEntries.stream()
.filter(bookEntry -> currentDate.isAfter(bookEntry.getPaymentDeadline()) && !bookEntry.isPaid() && !bookEntry.isNotified())
.forEach(BookEntry::notifyDebtor);
System.out.println("Finished second check..."); //this line never shows in one of runs and the task is never repeated again...
}
}
BookEntry
public class BookEntry {
private final UUID rentableId = UUID.randomUUID();
private final UUID personId;
private final UUID id;
private final BigDecimal amountDue;
private final LocalDate paymentDeadline;
private boolean paid = false;
private boolean notified = false;
public BookEntry(UUID personId, UUID id, BigDecimal amountDue, LocalDate paymentDeadline) {
this.personId = personId;
this.id = id;
this.amountDue = amountDue;
this.paymentDeadline = paymentDeadline;
}
public UUID getRentableId() {
return rentableId;
}
public UUID getPersonId() {
return personId;
}
public UUID getId() {
return id;
}
public BigDecimal getAmountDue() {
return amountDue;
}
public LocalDate getPaymentDeadline() {
return paymentDeadline;
}
public boolean isPaid() {
return paid;
}
public boolean isNotified() {
return notified;
}
public void settlePayment() {
if(!paid) {
paid = true;
}
else {
throw new IllegalStateException("This is already paid man!");
}
}
public void notifyDebtor() {
if(!notified) {
notified = true;
DataStock dataStock = DataStock.getInstance();
Person debtor = dataStock.getPeople().stream()
.filter(person -> person.getId().equals(personId))
.findFirst()
.orElseThrow();
debtor.alert(new TenantAlert(personId, rentableId, dataStock.getCurrentDate(), amountDue));
}
}
}

It seems that the answer is easy - whenever the task scheduled in ScheduledExecutorService throws an exception the task is halted and never repeated. Also the exception is not thrown visibly. The easiest way to avoid such situation is to have try-catch block in run() ,method of Runnable. Please have a look at this post: ScheduledExecutorService handling exceptions

Related

Why do tasks with and without ExecutorService take the same amount of time?

I have the following project structure in spring boot using JDBC template and executor service. The application takes almost the same amount of time with thread and without thread. I expected the time reduction to be at least one third.
I read the following from this link but I don know how to apply in my code:
In order to be able to use transactions with our thread we need to understand how transactions work with spring. Transaction information in spring is stored in ThreadLocal variables. Therefore these variables are specific for an ongoing transaction on a single thread.
Is my long-running task in SqlUtilities.executeSql(sqlName, values, dataSource) cause the problem?
Does anyone know why? Any solution is welcome.
#SpringBootApplication
public class ApplicationMain implements CommandLineRunner {
#Autowired
#Qualifier(value = "mainExecutorService")
public ExecutorService mainExecutorService;
#Autowired
#Qualifier(value = "mut")
public Mut mut;
#Autowired
#Qualifier(value = "apt")
public Apt apt;
public static void main(String[] args){
SpringApplication.run(ApplicationMain.class, args);
}
#Override
public void run(String... args) throws Exception {
try {
// First thread
mainExecutorService.submit(() -> {
mut.initialise("2019", "10", "31");
apt.sum();
}, true);
// Second thread
mainExecutorService.submit(() -> {
apt.initialise("2019", "10", "31");
mut.sum();
}, true);
mainExecutorService.shutdown();
if (mainExecutorService.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES))
System.out.println("done");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mainExecutorService.shutdownNow();
}
}
}
configuration
#Configuration
class ExecutorservicConfig {
#Bean("mainExecutorService")
public ExecutorService mainExecutorService() {
return Executors.newFixedThreadPool(2);
}
#Bean(name = "apt")
public Apt createApt(){
return new Apt();
}
#Bean(name = "mut")
public Mut createMut(){
return new Mut();
}
}
Interface
interface Som {
ExecutorService service = Executors.newCachedThreadPool();
void initialize (String year, String month, String day);
void sum();
void deleteInsertUpdate();
void execute();
void print();
}
First class considers being executed in the first thread
class Mut implements Som {
private String year, month, day ;
#Autowired
#Qualifier(value = "jdbcTemplate")
private JdbcTemplate jdbcTemplate;
private long staringProcessingTime;
#Override
public void initialize (String year, String month, String day) {
this.year = year;
this.month = month;
this.day = day;
}
#Override
public void sum() {
staringProcessingTime = System.currentTimeMillis();
System.out.println("Started first thread. starting time is:".concat(String.valueOf(staringProcessingTime)));
deleteInsertUpdate();
}
#Override
public void deleteInsertUpdate() {
String queryDelete = "";
String queryInsert = "";
String queryUpdate = "";
try {
service.submit(() -> jdbcTemplate.update(queryDelete));
service.submit(() -> jdbcTemplate.update(queryInsert));
service.submit(() -> jdbcTemplate.update(queryUpdate));
service.shutdown();
if (service.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES))
execute();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
service.shutdownNow();
}
}
#Override
public void execute() {
// long running task here ....
if( SqlUtilities.executeSql(sqlName, values, dataSource) )
print();
}
#Override
public void print() {
long endProcessingTime = System.currentTimeMillis();
System.out.println("Ended first thread. total time in millisecond:".concat(String.valueOf(endProcessingTime - staringProcessingTime)));
}
}
Second class considers being executed in the second thread.
class Apt implements Som {
private String year, month, day ;
private long staringProcessingTime;
#Autowired
#Qualifier(value = "jdbcTemplate")
private JdbcTemplate jdbcTemplate;
#Override
public void initialize (String year, String month, String day) {
this.year = year;
this.month = month;
this.day = day;
}
#Override
public void sum() {
staringProcessingTime = System.currentTimeMillis();
System.out.println("Started Second thread. starting time is:".concat(String.valueOf(staringProcessingTime)));
deleteInsertUpdate();
}
#Override
public void deleteInsertUpdate() {
String queryDelete = "";
String queryInsert = "";
String queryUpdate = "";
try {
service.submit(() -> jdbcTemplate.update(queryDelete));
service.submit(() -> jdbcTemplate.update(queryInsert));
service.submit(() -> jdbcTemplate.update(queryUpdate));
service.shutdown();
if (service.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES))
execute();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
service.shutdownNow();
}
}
#Override
public void execute() {
// long running task here ....
if( SqlUtilities.executeSql(sqlName, values, dataSource) )
print();
}
#Override
public void print() {
long endProcessingTime = System.currentTimeMillis();
System.out.println("Ended second thread. total time in millisecond:".concat(String.valueOf(endProcessingTime - staringProcessingTime)));
}
}
Utilities class
class SqlUtilities {
public static boolean executeSql(String sqlName, Map<String,String> values, DataSource dataSource){
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
boolean executed = false;
try{
Resource[] resources = resolver.getResources("classpath*:sql/*.sql");
for(Resource resource: resources)
{
if(sqlName.equals(resource.getFilename()))
{
File tempFile = File.createTempFile(resource.getFilename(), ".sql");
Path tempPath = Paths.get(tempFile.getAbsolutePath());
try (InputStream inputStream = resource.getInputStream()) {
FileUtils.copyInputStreamToFile(inputStream, tempFile);
String contents = new String(Files.readAllBytes(tempPath));
String newContents = StringSubstitutor.replace(contents, values);
Files.write(tempPath, Collections.singleton(newContents), StandardOpenOption.TRUNCATE_EXISTING);
Resource tempResource = new FileUrlResource(tempPath.toString());
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(tempResource);
databasePopulator.execute(dataSource);
} finally {
executed = Files.deleteIfExists(tempPath);
}
}
}
}catch (Exception e){
System.out.println(e.getMessage());;
}
return executed;
}

Why java swing does not allow changes from an ExecutorService Thread

I have a problem about my java GUI management. I'm making an updater but when i'm trying to edit some JText or JProgressbar from an ExecutorService Thread, nothing..
Why is it broken ? This is my ControllerManager:
public class UpdateManager extends AppManager {
private final List<Controller> controllers;
#Getter private final ExecutorService worker;
#Getter private final UserInterface form;
private boolean isEnd;
public UpdateManager() {
this.controllers = new ArrayList<>();
this.form = new Form(this);
this.worker = Executors.newCachedThreadPool();
}
#Override
public void start(Controller... controllers) {
form.initialize();
this.controllers.addAll(Arrays.asList(controllers));
for(Controller controller: controllers)
controller.start();
}
#Override
public void end(boolean dispose) {
if(isEnd) return;
for(Controller controller: controllers)
controller.end();
if(dispose) form.dispose();
isEnd = true;
}
}
And here is an example when i'm trying to change some labels:
/**
* Created by romain on 17/05/2015.
*/
public class ReleaseController implements Controller {
private final AppManager manager;
#Getter private final LinkedBlockingDeque<URL> files;
private Future<?> future;
private final SerializedObject<SerializedReleases> serializedReleases;
private final SerializedObject<Integer> serializedRelease, serializedTimestamp;
public ReleaseController(AppManager manager) {
this.manager = manager;
this.files = new LinkedBlockingDeque<>();
this.serializedReleases = SerializedObjectImpl.create(FileUtils.path("releases", "releases.dat"), true, null);
this.serializedRelease = SerializedObjectImpl.create(FileUtils.path("swtour", "release.int"), false, 0);
this.serializedTimestamp = SerializedObjectImpl.create(FileUtils.path("swtour", "timestamp.int"), false, 0);
}
/**
* TODO: checking local files
*/
#Override
public void start() {
this.future = manager.getWorker().submit(new Runnable() {
#Override
public void run() {
int release = serializedRelease.get();
int serverRelease = serializedReleases.get().lastRelease(AppUtils.OS);
int result = serverRelease - release;
if(result == 0 || result < 0) {
manager.getForm().alreadyUpdated(); //HERE
return;
}
for(int i=release+1;i<serverRelease;i++) {
try {
files.addLast(new URL(
FileUtils.path(Main.SERVER, "releases", AppUtils.OS.toString(), i + ".zip")));
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
serializedRelease.setObject(serverRelease).write();
serializedTimestamp.setObject((int)System.currentTimeMillis()).write();
}
});
}
#Override
public void end() {
if(future != null && !future.isCancelled())
future.cancel(true);
}
}
If you saw correctly, it:
manager.getForm().alreadyUpdated();
Modifies my GUI :
public void alreadyUpdated() {
content.getFirstLine().setText("");
content.getSecondLine().setText("Your client is already up-to-date!");
content.getPlayButton().setEnabled(true);
}
but nothing.. My gui is not changed !
Thank you
There are two problems with attempting to update a Swing data structure from another thread:
Swing is not designed to be multi-thread safe. You could corrupt its data structures.
There is nothing forcing it to see changes made in another thread. It can be running with values in registers or caches that are not affected by changes to memory.
As already noted in a comment, you need to use invokeAndWait or invokeLater to make your changes in the Swing event handling thread.

How do i set up a DelayQueue's Delay

I'm just starting out coding in java i'm in struggling with setting up a DelayQueue,
I wanted to have it so,
DelayQueue queue = new DelayQueue();
If (counter > 0){
queue.offer(Integer, *A custom delay*)
} Else {
queue.offer(Integer, *A different custom delay*)
}
I'm just trying to learn all the basics and ive read over the API and cant seem to grasp it.
Thanks in advance
this implementation of Delayed is good because:
implementation of compareTo() does not do any class casting, eliminatig the possibility of throwing a ClassCastException
implementation of compareTo() uses Math.min and Math.max functions before casting to int in order to properly prevent overflow errors
implementation of getDelay() properly converts the units and actually returns the time remaining
TestDelay class implements Delayed:
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class TestDelay implements Delayed
{
public final Long delayMillis;
public final Long expireTimeMillis;
public TestDelay(Long delayMillis)
{
this.delayMillis = delayMillis;
this.expireTimeMillis = System.currentTimeMillis()+delayMillis;
}
#Override
public final int compareTo(#NotNull Delayed o)
{
long diffMillis = getDelay(TimeUnit.MILLISECONDS)-o.getDelay(TimeUnit.MILLISECONDS);
diffMillis = Math.min(diffMillis,1);
diffMillis = Math.max(diffMillis,-1);
return (int) diffMillis;
}
#Override
public final long getDelay(#NotNull TimeUnit unit)
{
long delayMillis = expireTimeMillis-System.currentTimeMillis();
return unit.convert(delayMillis,TimeUnit.MILLISECONDS);
}
}
JUnit unit test showing an example of using the TestDelay class:
import org.junit.Test;
import java.util.concurrent.DelayQueue;
public class DelayQueueTest
{
#Test
public final void generalTest() throws InterruptedException
{
DelayQueue<TestDelay> q = new DelayQueue<>();
q.put(new TestDelay(500L));
q.put(new TestDelay(2000L));
q.put(new TestDelay(1000L));
q.put(new TestDelay(10L));
q.put(new TestDelay(3000L));
while (!q.isEmpty())
{
System.out.println(q.take().delayMillis);
}
}
}
output of DelayQueueTest:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class DelayQueueExample {
public static void main(String[] args) {
BlockingQueue<DelayedElement> blockingQueue = new DelayQueue<DelayedElement>();
try {
blockingQueue
.put(new DelayedElement(4000, "Message with delay 4s"));
blockingQueue
.put(new DelayedElement(2000, "Message with delay 2s"));
blockingQueue
.put(new DelayedElement(9000, "Message with delay 9s"));
} catch (InterruptedException ie) {
}
while (!blockingQueue.isEmpty()) {
try {
System.out.println(">>" + blockingQueue.take());
} catch (InterruptedException ie) {
}
}
}
}
class DelayedElement implements Delayed {
private long duration = 0;
private String message;
public DelayedElement(long duration, String name) {
this.duration = System.currentTimeMillis() + duration;
this.message = name;
}
#Override
public int compareTo(Delayed o) {
return (int) (this.duration - ((DelayedElement) o).getDuration());
}
#Override
/*
* Expiration occurs when an element's getDelay(TimeUnit unit) method
* returns a value less than or equal to zero.
*/
public long getDelay(TimeUnit unit) {
long diff = duration - System.currentTimeMillis();
return unit.convert(diff, TimeUnit.MILLISECONDS);
}
public long getDuration() {
return duration;
}
public void setDuration(long duration) {
this.duration = duration;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
#Override
public String toString() {
return "DelayedElement [duration=" + duration + ", message=" + message
+ "]";
}
}
Your "custom delay" classes must return the delay from the getDelay(TimeUnit timeUnit) method specified in the Delayed interface.
E.g.
public class MyClass implements Delayed {
public long getDelay(TimeUnit timeUnit) {
long delay = calculateDelaySomehow();
return delay;
}
}
Note that you also need to provide an implementation for compareTo().
The DelayQueue keeps the elements internally until a certain delay has expired. The elements must implement the interface java.util.concurrent.Delayed.
For example I have created a class DelayedTest extending Delayed interface. This will implement compareTo and getDelay() method
public class A{
public static void main(String... args){
DelayQueue dq=new DelayQueue();
DeleyedTest ob1=new DeleyedTest(10);
DeleyedTest ob2=new DeleyedTest(5);
DeleyedTest ob3=new DeleyedTest(15);
dq.offer(ob1);
dq.offer(ob2);
dq.offer(ob3);
Iterator itr=dq.iterator();
while(itr.hasNext()){
DeleyedTest dt=(DeleyedTest)itr.next();
System.out.println(dt.deleyTime);
}
}
}
class DeleyedTest implements Delayed{
public long deleyTime=0;
DeleyedTest(long deleyTime){
this.deleyTime=deleyTime;
}
#Override
public int compareTo(Delayed ob) {
if(this.deleyTime<((DeleyedTest)ob).deleyTime){
return -1;
}else if(this.deleyTime>((DeleyedTest)ob).deleyTime){
return 1;
}
return 0;
}
#Override
public long getDelay(TimeUnit unit) {
return unit.convert(deleyTime-System.currentTimeMillis(),TimeUnit.NANOSECONDS);
}
}
Result:
5
10
15

Concurrent tests: test case scenario automatization

Task definition: I need to test custom concurrent collection or some container which manipulates with collections in concurrent environment. More precisely - I've read-API and write-API. I should test if there is any scenarios where I can get inconsistent data.
Problem: All concurrent test frameworks (like MultiThreadedTC, look at MultiThreadedTc section of my question) just provides you an ability to control the asynchronous code execution sequence. I mean you should suppose a critical scenarios by your own.
Broad question: Is there frameworks that can take annotations like #SharedResource, #readAPI, #writeAPI and check if your data will always be consistent? Is that impossible or I just leak a startup idea?
Annotation: If there is no such framework, but you find the idea attractive, you are welcome to contact me or propose your ideas.
Narrow question: I'm new in concurrency. So can you suggest which scenarios should I test in the code below? (look at PeerContainer class)
PeerContainer:
public class PeersContainer {
public class DaemonThreadFactory implements ThreadFactory {
private int counter = 1;
private final String prefix = "Daemon";
#Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, prefix + "-" + counter);
thread.setDaemon(true);
counter++;
return thread;
}
}
private static class CacheCleaner implements Runnable {
private final Cache<Long, BlockingDeque<Peer>> cache;
public CacheCleaner(Cache<Long, BlockingDeque<Peer>> cache) {
this.cache = cache;
Thread.currentThread().setDaemon(true);
}
#Override
public void run() {
cache.cleanUp();
}
}
private final static int MAX_CACHE_SIZE = 100;
private final static int STRIPES_AMOUNT = 10;
private final static int PEER_ACCESS_TIMEOUT_MIN = 30;
private final static int CACHE_CLEAN_FREQUENCY_MIN = 1;
private final static PeersContainer INSTANCE;
private final Cache<Long, BlockingDeque<Peer>> peers = CacheBuilder.newBuilder()
.maximumSize(MAX_CACHE_SIZE)
.expireAfterWrite(PEER_ACCESS_TIMEOUT_MIN, TimeUnit.MINUTES)
.removalListener(new RemovalListener<Long, BlockingDeque<Peer>>() {
public void onRemoval(RemovalNotification<Long, BlockingDeque<Peer>> removal) {
if (removal.getCause() == RemovalCause.EXPIRED) {
for (Peer peer : removal.getValue()) {
peer.sendLogoutResponse(peer);
}
}
}
})
.build();
private final Striped<Lock> stripes = Striped.lock(STRIPES_AMOUNT);
private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1, new DaemonThreadFactory());
private PeersContainer() {
scheduledExecutorService.schedule(new CacheCleaner(peers), CACHE_CLEAN_FREQUENCY_MIN, TimeUnit.MINUTES);
}
static {
INSTANCE = new PeersContainer();
}
public static PeersContainer getInstance() {
return INSTANCE;
}
private final Cache<Long, UserAuthorities> authToRestore = CacheBuilder.newBuilder()
.maximumSize(MAX_CACHE_SIZE)
.expireAfterWrite(PEER_ACCESS_TIMEOUT_MIN, TimeUnit.MINUTES)
.build();
public Collection<Peer> getPeers(long sessionId) {
return Collections.unmodifiableCollection(peers.getIfPresent(sessionId));
}
public Collection<Peer> getAllPeers() {
BlockingDeque<Peer> result = new LinkedBlockingDeque<Peer>();
for (BlockingDeque<Peer> deque : peers.asMap().values()) {
result.addAll(deque);
}
return Collections.unmodifiableCollection(result);
}
public boolean addPeer(Peer peer) {
long key = peer.getSessionId();
Lock lock = stripes.get(key);
lock.lock();
try {
BlockingDeque<Peer> userPeers = peers.getIfPresent(key);
if (userPeers == null) {
userPeers = new LinkedBlockingDeque<Peer>();
peers.put(key, userPeers);
}
UserAuthorities authorities = restoreSession(key);
if (authorities != null) {
peer.setAuthorities(authorities);
}
return userPeers.offer(peer);
} finally {
lock.unlock();
}
}
public void removePeer(Peer peer) {
long sessionId = peer.getSessionId();
Lock lock = stripes.get(sessionId);
lock.lock();
try {
BlockingDeque<Peer> userPeers = peers.getIfPresent(sessionId);
if (userPeers != null && !userPeers.isEmpty()) {
UserAuthorities authorities = userPeers.getFirst().getAuthorities();
authToRestore.put(sessionId, authorities);
userPeers.remove(peer);
}
} finally {
lock.unlock();
}
}
void removePeers(long sessionId) {
Lock lock = stripes.get(sessionId);
lock.lock();
try {
peers.invalidate(sessionId);
authToRestore.invalidate(sessionId);
} finally {
lock.unlock();
}
}
private UserAuthorities restoreSession(long sessionId) {
BlockingDeque<Peer> activePeers = peers.getIfPresent(sessionId);
return (activePeers != null && !activePeers.isEmpty()) ? activePeers.getFirst().getAuthorities() : authToRestore.getIfPresent(sessionId);
}
public void resetAccessedTimeout(long sessionId) {
Lock lock = stripes.get(sessionId);
lock.lock();
try {
BlockingDeque<Peer> deque = peers.getIfPresent(sessionId);
peers.invalidate(sessionId);
peers.put(sessionId, deque);
} finally {
lock.unlock();
}
}
}
MultiThreadedTC test case sample: [optional section of question]
public class ProducerConsumerTest extends MultithreadedTestCase {
private LinkedTransferQueue<String> queue;
#Override
public void initialize() {
super.initialize();
queue = new LinkedTransferQueue<String>();
}
public void thread1() throws InterruptedException {
String ret = queue.take();
}
public void thread2() throws InterruptedException {
waitForTick(1);
String ret = queue.take();
}
public void thread3() {
waitForTick(1);
waitForTick(2);
queue.put("Event 1");
queue.put("Event 2");
}
#Override
public void finish() {
super.finish();
assertEquals(true, queue.size() == 0);
}
}
Sounds like a job for static analysis, not testing, unless you have time to run multiple trillions of test cases. You pretty much can't test multithreaded behaviour - test behaviour in a single thread, then prove the abscence of threading bugs.
Try:
http://www.contemplateltd.com/threadsafe
http://checkthread.org/

FutureTask with Callable that references the FutureTask

I have a situation where I need to create a FutureTask with a Callable that checks if it's owner has been cancelled. The code I have looks like this:
public static FutureTask<Result> makeFuture(final Call call, final TaskCompletionCallback completion) {
return new FutureTask<Result>(new Callable<Result>() {
#Override
public Result call() throws Exception {
Result result = CYLib.doNetworkRequest(call, new CarryOnCallback() {
#Override
public boolean shouldCarryOn() {
return !FutureTask.isDone();
}
});
return result;
}
});
}
Basically the doNetworkRequest asks the CarryOnCallback if it should continue at certain times during the operation. I would like for this callback to see if the FutureTask that is calling the doNetworkRequest was cancelled, which involves querying the actual FutureTask object.
Now I know that you can't really access 'this' because it hasn't been constructed yet. But is there a way around this, or a better design for my situation?
Cheers
EDIT:
Ok so I'm going about it like this now. Made a custom Callable and FutureTask. The Callable holds a reference to the FutureTask and this can be set manually after creating a new Callable:
public static MyTask makeMyTask(final Call call, final TaskCompletionCallback completion) {
MyTask task = null;
MyTask.InnerCallable innerCallable = new MyTask.InnerCallable(call, completion);
task = new MyTask(innerCallable);
innerCallable.setParent(task);
return task;
}
And just for reference, the InnerCallable looks like this:
public static class MyTask extends FutureTask<Result> {
InnerCallable callable;
public MyTask(InnerCallable callable) {
super(callable);
this.callable = callable;
}
private static class InnerCallable implements Callable<Result> {
private final Call call;
private final TaskCompletionCallback completion;
private WeakReference<MyTask> parentTask;
InnerCallable(Call call, TaskCompletionCallback completion) {
this.call = call;
this.completion = completion;
}
#Override
public Result call() {
Result result = CYLib.doNetworkRequest(this.call, new CarryOnCallback() {
#Override
public boolean shouldCarryOn() {
MyTask task = parentTask.get();
return !(task == null || task.isCancelled());
}
});
return result;
}
private void setParent(MyTask parentTask) {
this.parentTask = new WeakReference<MyTask>(parentTask);
}
}
}
So, your CYLib.doNetworkRequest() is working in another thread?
private static Map<Call,FutureTask> map=new HashMap();
public static FutureTask<Result> makeFuture(final Call call, final TaskCompletionCallback completion) {
FutureTask<Result> futureResult = new FutureTask<Result>(new Callable<Result>() {
#Override
public Result call() throws Exception {
Result result = CYLib.doNetworkRequest(call, new CarryOnCallback() {
#Override
public boolean shouldCarryOn() {
return !map.get(call).isCancelled();
}
});
return result;
}
});
map.put(call,futureResult);
return futureResult;
}

Categories

Resources