I would like to get some advice on a simple multithreading system I am designing.
The idea:
The application is capturing frames and displaying them in 1st imageview. These captured frames are also being processed (by MyHandDetectionThread) and then displayed in 2nd imageview.
My solution:
public class VideoManager {
private volatile BufferLinkedList<InputFrame> mInputFrames;
private volatile BufferLinkedList<ProcessedFrame> mProcessedFrames;
private static VideoManager mVideoManagerInstance = new VideoManager();
private Timer captureTimer;
private MyVideoCaptureThread myVideoCaptureThread;
private MyFrameDisplayThread myFrameDisplayThread;
private MyHandDetectionThread myHandDetectionThread;
private MyProcessedFrameDisplayThread myProcessedFrameDisplayThread;
private enum ThreadMessages {
PROCESS_INPUT_FRAME,
NEW_INPUT_FRAME,
NEW_PROCESSED_FRAME_ARRIVED,
GET_NEW_FRAME
}
public static VideoManager getInstance() {
if (mVideoManagerInstance == null) {
mVideoManagerInstance = new VideoManager();
}
return mVideoManagerInstance;
}
// not visible constructor - for singleton purposes
private VideoManager() {
mInputFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
mProcessedFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
}
public void startDetectionAndRecognition(ImageView camIV, ImageView handIV) {
mInputFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
mProcessedFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
captureTimer = new Timer();
myVideoCaptureThread = new MyVideoCaptureThread();
myFrameDisplayThread = new MyFrameDisplayThread(camIV, handIV);
myHandDetectionThread = new MyHandDetectionThread();
myProcessedFrameDisplayThread = new MyProcessedFrameDisplayThread();
captureTimer.schedule(new TimerTask() {
public void run() {
if (myVideoCaptureThread != null && myVideoCaptureThread.threadMessages != null)
myVideoCaptureThread.threadMessages.offer(ThreadMessages.GET_NEW_FRAME);
}
}, 0, 1000 / Config.fps);
myFrameDisplayThread.start();
myVideoCaptureThread.start();
myHandDetectionThread.start();
myProcessedFrameDisplayThread.start();
}
public void stop() {
captureTimer.cancel();
myVideoCaptureThread.interrupt();
myHandDetectionThread.interrupt();
myFrameDisplayThread.interrupt();
myGestureRecogitionThread.interrupt();
mInputFrames.removeAll(mInputFrames);
mProcessedFrames.removeAll(mProcessedFrames);
isActive = false;
}
public boolean isActive() {
return isActive;
}
////////////////////////
// Thread clases
////////////////////////
private class MyVideoCaptureThread extends Thread {
LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(128);
#Override
public void run() {
WebCamVideoCapture vc = new WebCamVideoCapture();
while (!isInterrupted()) {
if (threadMessages != null && threadMessages.poll() == ThreadMessages.GET_NEW_FRAME) {
Mat mat = vc.getNextMatFrame();
if (mat != null && mInputFrames != null) {
mInputFrames.offerFirst(new InputFrame(mat));
if (myFrameDisplayThread != null && myFrameDisplayThread.threadMessages != null)
myFrameDisplayThread.threadMessages.offer(ThreadMessages.NEW_INPUT_FRAME);
if (myHandDetectionThread != null && myHandDetectionThread.threadMessages != null)
myHandDetectionThread.threadMessages.offer(ThreadMessages.PROCESS_INPUT_FRAME);
}
}
}
vc.close();
}
}
private class MyFrameDisplayThread extends Thread {
LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(128);
ImageView mCamImageView;
long lastUpdatedCamImageViewMillis;
long lastUpdatedHandImageViewMillis;
public MyFrameDisplayThread(ImageView mImageView) {
this.mCamImageView = mImageView;
}
private synchronized void updateImageViews() {
if (threadMessages.poll() == ThreadMessages.NEW_INPUT_FRAME && mInputFrames != null && !mInputFrames.isEmpty() && mInputFrames.peek() != null && mInputFrames.peek().getFrame() != null) {
if(Config.IS_DEBUG) System.out.println("Updating frame image view");
mCamImageView.setImage(Utils.cvMatToImage(mInputFrames.peekFirst().getFrame()));
}
}
#Override
public void run() {
while (!isInterrupted()) {
updateImageViews();
}
}
}
private class MyHandDetectionThread extends Thread {
LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(128); //TODO if multiple threads, define it out of class
HandDetector hd = new HandDetector();
#Override
public void run() {
while (!isInterrupted()) {
if (threadMessages.poll() == ThreadMessages.PROCESS_INPUT_FRAME && mInputFrames != null && mInputFrames.size() > 0 && mInputFrames.peek() != null) {
if(Config.IS_DEBUG) System.out.println("Detecting hand...");
mProcessedFrames.offerFirst(new ProcessedFrame(hd.detectHand(mInputFrames.peek()), null, null, null));
if (myGestureRecogitionThread != null && myGestureRecogitionThread.threadMessages != null)
myGestureRecogitionThread.threadMessages.offer(ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED);
if(myFrameDisplayThread != null && myFrameDisplayThread.threadMessages != null)
myFrameDisplayThread.threadMessages.offer(ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED);
}
}
}
}
private class MyProcessedFrameDisplayThread extends Thread {
LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(128);
ImageView mHandImageView;
public MyProcessedFrameDisplayThread(ImageView mHandImageView) {
mHandImageView = mHandImageView;
}
private synchronized void updateImageViews() {
if(threadMessages.poll() == ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED && mProcessedFrames != null && !mProcessedFrames.isEmpty() && mProcessedFrames.peek() != null && mProcessedFrames.peek().getmHandMask() != null) {
if(Config.IS_DEBUG) System.out.println("Updating hand image view");
mHandImageView.setImage(Utils.cvMatToImage(mProcessedFrames.peekFirst().getmHandMask()));
}
}
#Override
public void run() {
while (!isInterrupted())
if (threadMessages.poll() == ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED)
updateImageViews();
}
}
}
public class BufferLinkedList<E> extends LinkedList<E> {
private int counter = 0;
private int sizeLimit = 48;
public BufferLinkedList(int sizeLimit) {
this.sizeLimit = sizeLimit;
}
#Override
public synchronized boolean offerFirst(E e) {
while(size() > sizeLimit) {
removeLast();
}
return super.offerFirst(e);
}
#Override
public synchronized E peekFirst() {
return super.peekFirst();
}
#Override
public synchronized E peekLast() {
return super.peekLast();
}
#Override
public synchronized E pollFirst() {
return super.pollFirst();
}
#Override
public synchronized E pollLast() {
return super.pollLast();
}
}
My problems:
The frames are not displayed smoothly. there are a irregular, 1-5 seconds breaks between methods updating imageviews are fired. However the MyHandDetectionThread's task runs preaty quickly. And sizes of message queues of Display Threads are increasing fast. Maybe this is because of some locks on lists storing the frames?
Question:
Is my solution correct? Are there some design patterns describing this scenario? Do you have some suggestions for improvement?
Edit:
I've added waiting and notifying in thread loops. Results are satisfying. The CPU cost is now ~30% comparing to ~80% before. Everything runs more stable and smoothier. However, I'm not familiar with waiting and notifying aproach. So let me know if you find something stupid in my code.
public class VideoManager {
private volatile BufferLinkedList<InputFrame> mInputFrames;
private volatile BufferLinkedList<ProcessedFrame> mProcessedFrames;
private static VideoManager mVideoManagerInstance = new VideoManager();
private Timer captureTimer;
private MyVideoCaptureThread myVideoCaptureThread;
private MyFrameDisplayThread myFrameDisplayThread;
private MyHandDetectionThread myHandDetectionThread;
private MyGestureRecogitionThread myGestureRecogitionThread;
private MySkinDisplayThread mySkinDisplayThread;
private final static int THREAD_MESSAGES_LIMIT = 10000;
private final static int TIMER_INTERVAL = 1000 / Config.fps;
private final static int WAITING_TIMEOUT = 2000;
private enum ThreadMessages {
PROCESS_INPUT_FRAME,
NEW_INPUT_FRAME,
NEW_PROCESSED_FRAME_ARRIVED,
GET_NEW_FRAME
}
public static VideoManager getInstance() {
if (mVideoManagerInstance == null) {
mVideoManagerInstance = new VideoManager();
}
return mVideoManagerInstance;
}
// not visible constructor - for singleton purposes
private VideoManager() {
mInputFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
mProcessedFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
}
public void startDetectionAndRecognition(ImageView camIV, ImageView handIV) {
mInputFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
mProcessedFrames = new BufferLinkedList<>(Config.inputFramesListLimit);
captureTimer = new Timer();
myFrameDisplayThread = new MyFrameDisplayThread(camIV);
myVideoCaptureThread = new MyVideoCaptureThread();
myHandDetectionThread = new MyHandDetectionThread();
myGestureRecogitionThread = new MyGestureRecogitionThread();
mySkinDisplayThread = new MySkinDisplayThread(handIV);
myVideoCaptureThread.start();
captureTimer.schedule(new TimerTask() {
public void run() {
if (myVideoCaptureThread != null && myVideoCaptureThread.threadMessages != null) {
myVideoCaptureThread.threadMessages.offer(ThreadMessages.GET_NEW_FRAME);
System.out.println("Timer get frame request sent");
myVideoCaptureThread.wakeUp();
}
}
}, 0, TIMER_INTERVAL);
myFrameDisplayThread.start();
mySkinDisplayThread.start();
myHandDetectionThread.start();
myGestureRecogitionThread.start();
}
public void stop() {
captureTimer.cancel();
myVideoCaptureThread.interrupt();
myHandDetectionThread.interrupt();
mySkinDisplayThread.interrupt();
myFrameDisplayThread.interrupt();
myGestureRecogitionThread.interrupt();
mInputFrames.removeAll(mInputFrames);
mProcessedFrames.removeAll(mProcessedFrames);
}
////////////////////////
// Lock class
////////////////////////
private static final class Lock {}
////////////////////////
// Thread clases
////////////////////////
private class MyVideoCaptureThread extends Thread {
volatile LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(THREAD_MESSAGES_LIMIT);
WebCamVideoCapture vc = new WebCamVideoCapture();
Lock lock = new Lock();
#Override
public void run() {
synchronized (lock) {
while (!isInterrupted()) {
if (threadMessages.poll() != ThreadMessages.GET_NEW_FRAME) {
try {
lock.wait(WAITING_TIMEOUT);
System.out.println("WideoCaptureThread waiting");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Mat mat = vc.getNextMatFrame();
System.out.println("getting next frame from webcam");
if (mat != null && mInputFrames != null) {
mInputFrames.offerFirst(new InputFrame(vc.getNextMatFrame()));
if (myHandDetectionThread != null && myHandDetectionThread.threadMessages != null) {
myHandDetectionThread.wakeUp();
myHandDetectionThread.threadMessages.offer(ThreadMessages.PROCESS_INPUT_FRAME);
}
if (myFrameDisplayThread != null && myFrameDisplayThread.threadMessages != null) {
myFrameDisplayThread.wakeUp();
myFrameDisplayThread.threadMessages.offer(ThreadMessages.NEW_INPUT_FRAME);
}
}
}
}
}
public void wakeUp() {
synchronized (lock) {
lock.notifyAll();
System.out.println("Waking up WideoCapture");
}
}
#Override
public void interrupt() {
vc.close();
super.interrupt();
}
}
private class MyFrameDisplayThread extends Thread {
volatile LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(THREAD_MESSAGES_LIMIT);
Lock lock = new Lock();
ImageView mCamImageView;
public MyFrameDisplayThread(ImageView mImageView) {
this.mCamImageView = mImageView;
}
private void updateImageViews() {
if (shouldUpdateCamImageView() && mInputFrames != null && !mInputFrames.isEmpty() && mInputFrames.peek() != null && mInputFrames.peek().getFrame() != null) {
System.out.println("Updating frame image view");
mCamImageView.setImage(Utils.cvMatToImage(mInputFrames.peekFirst().getFrame()));
threadMessages.poll();
}
}
#Override
public void run() {
synchronized (lock) {
while (!isInterrupted()) {
if (threadMessages.peek() != ThreadMessages.NEW_INPUT_FRAME) {
try {
lock.wait(WAITING_TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
updateImageViews();
}
}
}
public void wakeUp() {
synchronized (lock) {
lock.notifyAll();
System.out.println("Waking up FrameDisplay");
}
}
private boolean shouldUpdateCamImageView() {
if (!Config.CAPTURE_PREVIEW_MODE) return false;
return true;
}
}
private class MySkinDisplayThread extends Thread {
volatile LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(THREAD_MESSAGES_LIMIT);
ImageView mHandImageView;
Object lock = new Lock();
public MySkinDisplayThread(ImageView mHandImageView) {
this.mHandImageView = mHandImageView;
}
private synchronized void updateHandImageView() {
if (shouldUpdateHandImageView() && mProcessedFrames != null && !mProcessedFrames.isEmpty() && mProcessedFrames.peek() != null && mProcessedFrames.peek().getmHandMask() != null) {
System.out.println("Updating skin image view");
mHandImageView.setImage(Utils.cvMatToImage(mProcessedFrames.peekFirst().getmHandMask()));
threadMessages.poll();
}
}
#Override
public void run() {
synchronized (lock) {
while (!isInterrupted()) {
if (threadMessages.peek() != ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED) {
try {
lock.wait(WAITING_TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
updateHandImageView();
}
}
}
private boolean shouldUpdateHandImageView() {
if (!Config.SKIN_MASK_PREVIEW_MODE) return false;
return true;
// long now = System.currentTimeMillis();
// boolean should = now - lastUpdatedHandImageViewMillis > TIMER_INTERVAL;
// lastUpdatedHandImageViewMillis = now;
// return should;
}
public void wakeUp() {
synchronized (lock) {
lock.notifyAll();
System.out.println("Waking up FrameDisplay");
}
}
}
private class MyHandDetectionThread extends Thread {
volatile LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(THREAD_MESSAGES_LIMIT); //TODO if multiple threads, define it out of class
HandDetector hd = new HandDetector();
Object lock = new Lock();
#Override
public void run() {
synchronized (lock) {
while (!isInterrupted()) {
if (threadMessages.poll() != ThreadMessages.PROCESS_INPUT_FRAME) {
try {
lock.wait(WAITING_TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (mInputFrames != null /*&& mInputFrames.size() > 0 && mInputFrames.peek() != null && !mInputFrames.peek().getIsProcessed()*/) {
System.out.println("Detecting hand...");
// Mat handMask = hd.detectHand(mInputFrames.peek());
// int[][] fingerCoordinates = new int[5][2];
// int[] convDefects = new int[5];
// int[] handCenterCoordinates = new int[2];
mProcessedFrames.offerFirst(new ProcessedFrame(hd.detectHand(mInputFrames.peek()), null, null, null));
if (myGestureRecogitionThread != null && myGestureRecogitionThread.threadMessages != null) {
myGestureRecogitionThread.threadMessages.offer(ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED);
mySkinDisplayThread.wakeUp();
}
if (mySkinDisplayThread != null && mySkinDisplayThread.threadMessages != null) {
mySkinDisplayThread.threadMessages.offer(ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED);
mySkinDisplayThread.wakeUp();
}
}
}
}
}
public void wakeUp() {
synchronized (lock) {
lock.notifyAll();
System.out.println("Waking up hand Detection");
}
}
}
private class MyGestureRecogitionThread extends Thread {
volatile LinkedBlockingQueue<ThreadMessages> threadMessages = new LinkedBlockingQueue<>(THREAD_MESSAGES_LIMIT);
GestureRecognizer r = new GestureRecognizer();
Lock lock = new Lock();
#Override
public void run() {
synchronized (lock) {
while (!isInterrupted()) {
if (threadMessages.poll() != ThreadMessages.NEW_PROCESSED_FRAME_ARRIVED) {
try {
lock.wait(WAITING_TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
r.lookForGestures(mProcessedFrames);
}
}
}
}
public void wakeUp() {
synchronized (lock) {
lock.notifyAll();
System.out.println("Waking up hand Detection");
}
}
}
}
Both threads seem to use polling in their run() method; i.e. they continuously loop around a statement checking a boolean condition. This can be bad for CPU usage, because a single thread can lock up the CPU without giving any cycles to other threads; it can end up hogging the CPU, even though it's not doing anything too useful; just failing some boolean condition.
You should use an asynchronous method for communicating with Threads; rather than using a polling mechanism, you should put threads to sleep when they are not required to do any processing, and wake them up once they're needed. This allows threads to yield the CPU, meaning that they willingly relinquish their active context so that other threads can execute.
Related
I have a class extending Thread. Inside this run I have a few method working in while loop. Now I want one of this methods gameSurface.updateBitmapObjects() to work with delay. How to achieve this? Here is my code:
GameThread.java:
public class GameThread<T extends GameSurface> extends Thread {
private boolean isRunning;
private long startTime, loopTime;
private long delay = 33;
private SurfaceHolder surfaceHolder;
private T gameSurface;
public GameThread(SurfaceHolder surfaceHolder, T gameSurface) {
this.surfaceHolder= surfaceHolder;
this.gameSurface = gameSurface;
isRunning = true;
}
#Override
public void run() {
while(isRunning) {
startTime = SystemClock.uptimeMillis();
Canvas canvas = surfaceHolder.lockCanvas(null);
if(canvas != null) {
synchronized (surfaceHolder) {
gameSurface.updateBitmapObjects();
gameSurface.drawMapBitmap(canvas);
((GameMainSurface)gameSurface).drawCoinAndCoins();
((GameMainSurface)gameSurface).drawFieldLines(canvas);
((GameMainSurface)gameSurface).updatePlayersLabels(canvas);
if (((GameMainSurface)gameSurface).isGameMainFragment()) {
((GameMainSurface)gameSurface).setSomeValues();
}
gameSurface.drawObjects(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
loopTime = SystemClock.uptimeMillis() - startTime;
if(loopTime < delay) {
try {
Thread.sleep(delay - loopTime);
}
catch (InterruptedException e) {
Log.e("Interupted ex", e.getMessage());
}
}
}
}
}
GameMainSurface.java:
(...)
public void updateBitmapObjects() {
this.chibi1.update();
(...)
}
MovingObject.java:
// I want this method to work with delay let's say 100 ms
public void update() {
this.colUsing++;
if (colCount == 13) {
if (colUsing >= 9) {
if (rowUsing == ROW_TOP_TO_BOTTOM_CHIBI || rowUsing == ROW_BOTTOM_TO_TOP_CHIBI)
colUsing = 1;
else
colUsing = 0;
}
}
if (colCount == 4) {
if (colUsing >= 4) {
this.colUsing = 0;
}
}
}
case:
considered you have a queue with tasks(task1,task2,task3,task1,task2,task3,...),
how can I excecute the tasks in the queue by using exactly 2 threads.
requirement:
1.one thread should execute task1 and task2, anthoner should do task3, or conversely
because some limited conditions eg. limited outside resources
2.task3 should always be executed after finishing to execute task1 and task2
in the exactly order of the queue
3.should consider the diff situation ,eg the time consuming of task1,task2
and task3 may be totally different
4.should` not come out dead loop
figure for flow
CODE
public class testRunManager {
public static void main(String[] args){
ConcurrentLinkedQueue<BaseTask> tasks = new ConcurrentLinkedQueue<>();
for (int index = 0; index < 10; index++) {
tasks.add(new Task1());
tasks.add(new Task2());
tasks.add(new Task3());
}
BaseRunManager.getInstance().addTasks(tasks);
Thread thread1 = BaseRunManager.getInstance().getNewThread(TaskThread.Type.BeforeWards);
Thread thread2 = BaseRunManager.getInstance().getNewThread(TaskThread.Type.AfterWards);
//start
thread1.start();
thread2.start();
}
}
public class TaskThread extends Thread{
enum Type{
BeforeWards,
AfterWards
}
Type type;
public TaskThread(Type type,Runnable runnable){
super(runnable);
this.type = type;
}
}
public interface ShouldRunBeforeWardsJob {
}
public interface ShouldRunAfterWardsJob {
}
abstract public class RunController {
public enum Performance {
BUSYING,
RUNNING,
PAUSED,
}
protected enum ControlState {
PAUSING,
PAUSED,
STOPING,
RESUMING,
RUNNING,
STEPPING,
}
private ControlState state = ControlState.RUNNING;
private Performance performance = Performance.BUSYING;
private List<ControlListener> listeners = new ArrayList<>();
protected ReentrantLock controlLock = new ReentrantLock();
protected Condition controlCondition = controlLock.newCondition();
public Performance getPerformance() {
return performance;
}
protected ControlState getState() {
return state;
}
public void addListener(ControlListener listener) {
listeners.add(listener);
}
public void removeListener(ControlListener listener) {
listeners.remove(listener);
}
public void pause() {
if (performance != Performance.RUNNING) {
return;
}
setState(ControlState.PAUSING);
}
public void step() {
if (performance != Performance.PAUSED) {
return;
}
setState(ControlState.STEPPING);
}
public void stop() {
if (performance != Performance.RUNNING && performance != Performance.PAUSED) {
return;
}
setState(ControlState.STOPING);
}
public void resume() {
if (performance != Performance.PAUSED) {
return;
}
setState(ControlState.RESUMING);
}
private void setPerformance(Performance p) {
if (performance != p) {
Performance old = this.performance;
this.performance = p;
for (ControlListener cl : listeners) {
cl.performChanged(old, p);
}
}
}
protected void setState(ControlState state) {
controlLock.lock();
try {
this.state = state;
switch (this.state) {
case RESUMING:
case STEPPING:
case PAUSING:
case STOPING:
controlCondition.signal();
setPerformance(Performance.BUSYING);
break;
case PAUSED:
setPerformance(Performance.PAUSED);
break;
case RUNNING:
setPerformance(Performance.RUNNING);
}
}finally {
controlLock.unlock();
}
}
public interface ControlListener {
void performChanged(Performance oldState, Performance newState);
}
}
public abstract class BaseTask {
enum State{
FINISH,
NOT
}
protected State state;
public State getState(){
return state;
}
public void setState(State state){
this.state = state;
}
abstract void runJob();
abstract void doJob();
}
public class BaseRunManager {
private static BaseRunManager instance;
private ConcurrentLinkedQueue<BaseTask> tasks = new
ConcurrentLinkedQueue<>();
public synchronized static BaseRunManager getInstance(){
if(instance == null){
instance = new BaseRunManager();
}
return instance;
}
public BaseRunManager(){
}
public void addTasks(ConcurrentLinkedQueue<BaseTask> tasks){
this.tasks = tasks;
}
public Thread getNewThread(TaskThread.Type type){
return new TaskThread(type,new BaseRunnable());
}
private class BaseRunnable extends RunController implements Runnable{
private BaseTask curTask;
private final AtomicBoolean afterwardsFinish = new AtomicBoolean(true);
private final AtomicInteger beforewardsFinishNum = new AtomicInteger(0);
private final AtomicInteger currentThreadNum = new AtomicInteger(0);
private final Condition condition = controlLock.newCondition();
#Override
public void run() {
currentThreadNum.incrementAndGet();
TaskThread curThread = (TaskThread)Thread.currentThread();
while (tasks.size()>0) {
//get task
controlLock.lock();
try {
curTask = tasks.peek();
if ((curTask instanceof ShouldRunBeforeWardsJob && curThread.type == TaskThread.Type.BeforeWards)
|| (curTask instanceof ShouldRunAfterWardsJob && curThread.type == TaskThread.Type.AfterWards)) {
tasks.poll();
if (curTask instanceof ShouldRunBeforeWardsJob) {
curTask.runJob();
beforewardsFinishNum.incrementAndGet();
condition.signalAll();
} else if (curTask instanceof ShouldRunAfterWardsJob) {
if (beforewardsFinishNum.get() / 2 != 0) {
condition.await();
curTask.runJob();
}
}
} else {
condition.awaitNanos(20);
continue;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
controlLock.unlock();
}
}
}
}
}
Here is another approach:
public static void main(String[] args) {
final BlockingQueue<BaseTask> tasks = new LinkedBlockingQueue<>();
final BlockingQueue<BaseTask> queue2 = new LinkedBlockingQueue<>();
for (int index = 0; index < 10; index++) {
tasks.add(new BaseTask("Task1"));
tasks.add(new BaseTask("Task2"));
tasks.add(new BaseTask("Task3"));
}
Thread thread1 = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
try {
BaseTask task = tasks.take();
task.run();
task = tasks.take();
task.run();
task = tasks.take();
queue2.offer(task);
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
try {
BaseTask task = queue2.take();
task.run();
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
thread2.start();
thread1.start();
}
private static class BaseTask implements Runnable {
private final String name;
public BaseTask(String name) {
this.name = name;
}
#Override
public void run() {
System.out.println(name + " ["
+ Thread.currentThread().getName() + "]");
}
}
In our latest project we have tried some thing like this.You can put dependent task in single worker thread so they will execute sequentially.
public interface SerializedRunnable extends Runnable {
int getKey();
}
public void execute(Runnable command) {
final int key;
if (command instanceof SerializedRunnable ) {
key = ((SerializedRunnable ) command).getKey();
}
final int index =Math.abs(key) % size;
workers[index].execute(command);
I am writing an apk analysis program. I cannot go deep into details because it's research material. However, the point is that from time to time when I execute the analysis routine, I get the following message:
Exception: java.lang.NullPointerException thrown from the UncaughtExceptionHandler in thread "main"
This happens if I do not override the UncaughtExceptionHandler and also if I do, as you'll see from the code below. Since there is no stacktrace data I cannot know where that exception is coming from and what its cause really is.
I hope you can help me...
This is only the main code. There are three main operation that I have to execute. In order not to disclose particular information, I renamed them to A, B and C
public class MainScannerSequential {
public static void main(String[] args) throws ParserConfigurationException, IOException, InterruptedException {
final File target = new File(args[1]);
final File result = new File(args[2]);
final Options options = new Options(args);
silentMode = options.contains("-s");
noA = options.contains("-nl");
noC = options.contains("-ne");
aStrategy = Factory.createA();
bScanner = Factory.createB();
cDetector = Factory.createcC();
examinedFiles = new PersistentFileList(Globals.EXAMINED_FILES_LIST_FILE);
if (result.exists())
resultsWriter = new BufferedWriter(new FileWriter(result, true));
else {
resultsWriter = new BufferedWriter(new FileWriter(result));
resultsWriter.write("***");
resultsWriter.newLine();
}
if (Globals.PERFORMANCE_FILE.exists())
performancesWriter = new BufferedWriter(new FileWriter(Globals.PERFORMANCE_FILE, true));
else {
performancesWriter = new BufferedWriter(new FileWriter(Globals.PERFORMANCE_FILE));
performancesWriter.write("***");
performancesWriter.newLine();
}
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread t, Throwable e) {
if ((t != null) && (t.getName() != null))
System.out.println("In thread : " + t.getName());
if ((e != null) && (e.getMessage() != null)) {
System.out.println(e.getClass().getName() + ": " + e.getMessage());
if (e.getStackTrace() != null)
for (StackTraceElement ste : e.getStackTrace())
if (ste != null)
System.out.println(ste.getFileName() + " at line " + ste.getLineNumber());
}
}
});
if (target.isDirectory()) {
enumerateDirectory(target);
} else {
String name = target.getName().toLowerCase();
if (name.endsWith(".apklist"))
readFileList(target);
else if (name.endsWith(".apk"))
checkFile(target);
}
closeWriters();
}
private static void println(String message) {
if (!silentMode)
System.out.println(message);
}
private static void print(String message) {
if (!silentMode)
System.out.print(message);
}
private static void enumerateDirectory(File directory) {
for (File file : directory.listFiles()) {
checkFile(file);
if (file.isDirectory())
enumerateDirectory(file);
}
}
private static void readFileList(File file) {
try {
BufferedReader reader = new BufferedReader(new FileReader(file));
String line = null;
while((line = reader.readLine()) != null) {
File readFile = new File(line);
if (readFile.exists())
checkFile(readFile);
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void checkFile(File file) {
if (examinedFiles.contains(file)) {
println("Skipped: " + file.getName());
return;
}
if (!file.getName().toLowerCase().endsWith(".apk")) {
return;
}
final Wrapper<Double> unpackingTime = new Wrapper<Double>(0.0);
final ApplicationData data = unpack(file, unpackingTime);
if (data == null) {
return;
}
scanData(data, unpackingTime.value);
}
private static ApplicationData unpack(final File file, final Wrapper<Double> unpackingTime) {
final Wrapper<ApplicationData> resultWrapper = new Wrapper<ApplicationData>(null);
final Wrapper<Exception> exceptionWrapper = new Wrapper<Exception>(null);
println("Unpacking: " + file.getName());
unpackingTime.value = Stopwatch.time(new Runnable() {
#Override
public void run() {
try {
resultWrapper.value = ApplicationData.open(file);
} catch (Exception e) {
exceptionWrapper.value = e;
}
}
});
if (resultWrapper.value != null)
println("Unpacked: " + file.getName());
else if (exceptionWrapper.value != null)
println("Dropped: " + file.getName() + " : " + exceptionWrapper.value.getMessage());
return resultWrapper.value;
}
private static void scanData(final ApplicationData applicationData, Double unpackingTime) {
String apkName = applicationData.getDecodedPackage().getOriginalApk().getAbsolutePath();
println("Submitted: " + apkName);
examinedFiles.add(applicationData.getDecodedPackage().getOriginalApk());
final Wrapper<Boolean> aDetected = new Wrapper<Boolean>(false);
final Wrapper<Result> bDetected = new Wrapper<Result>(new Result());
final Wrapper<Boolean> cDetected = new Wrapper<Boolean>(false);
final Wrapper<Double> aDetectionTime = new Wrapper<Double>((double)ANALYSIS_TIMEOUT);
final Wrapper<Double> bDetectionTime = new Wrapper<Double>((double)ANALYSIS_TIMEOUT);
final Wrapper<Double> cDetectionTime = new Wrapper<Double>((double)ANALYSIS_TIMEOUT);
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.submit(new Runnable() {
#Override
public void run() {
textDetectionTime.value = Stopwatch.time(new Runnable() {
#Override
public void run() {
bScanner.setUnpackedApkDirectory(applicationData.getDecodedPackage().getDecodedDirectory());
bDetected.value = bScanner.evaluate();
}
});
}
});
if (!noA)
executor.submit(new Runnable() {
#Override
public void run() {
lockDetectionTime.value = Stopwatch.time(new Runnable() {
#Override
public void run() {
aStrategy.setTarget(applicationData.getDecodedPackage());
aDetected.value = aStrategy.detect();
}
});
}
});
if (!noC)
executor.submit(new Runnable() {
#Override
public void run() {
encryptionDetectionTime.value = Stopwatch.time(new Runnable() {
#Override
public void run() {
cDetector.setTarget(applicationData.getDecodedPackage());
cDetected.value = cDetector.detect();
}
});
}
});
boolean timedOut = false;
executor.shutdown();
try {
if (!executor.awaitTermination(ANALYSIS_TIMEOUT, TimeUnit.SECONDS)) {
executor.shutdownNow();
timedOut = true;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
resultsWriter.write(String.format("%s, %b, %b, %f, %b, \"%s\", %b\n",
apkName,
aDetected.value,
bDetected.value.isAccepted(),
bDetected.value.getScore(),
cDetected.value,
bDetected.value.getComment(),
timedOut));
performancesWriter.write(String.format("%s, %f, %f, %f, %f, %d, %dl, %dl\n",
apkName,
aDetectionTime.value,
bDetectionTime.value,
cDetectionTime.value,
unpackingTime,
applicationData.getSmaliLoader().getClassesCount(),
applicationData.getSmaliLoader().getTotalClassesSize(),
applicationData.getDecodedPackage().getOriginalApk().length()));
resultsWriter.flush();
performancesWriter.flush();
} catch (IOException e) { }
// No longer deleting temp files
if (!timedOut)
println("Completed: " + apkName);
else {
print("Timeout");
if (bDetectionTime.value == 0) print(" TextDetection");
if (!noA && (aDetectionTime.value == 0)) print(" LockDetection");
if (!noC && (cDetectionTime.value == 0)) print(" EncryptionDetection");
println(": " + apkName);
}
}
private static void closeWriters() throws IOException {
resultsWriter.close();
performancesWriter.close();
examinedFiles.dispose();
}
private static final int ANALYSIS_TIMEOUT = 40; // seconds
private static Boolean silentMode = false;
private static Boolean noA = false;
private static Boolean noC = false;
private static PersistentFileList examinedFiles;
private static BufferedWriter resultsWriter;
private static BufferedWriter performancesWriter;
private static A aStrategy;
private static B bScanner;
private static C cDetector;
}
I labeled the question as multithread because the same happens in the multithreaded version, which I serialized using this code in order to debug that error. Notice that the NullPointerException is in thread main, not in other Executor-spawned threads.
If you don't have a stack trace, it could mean that there are some crazy exceptions thrown in your code (see Exception without stack trace in Java), but it is more likely that the JVM is re-using exception objects as a performance optimization and the -XX:-OmitStackTraceInFastThrow JVM option would help you.
See this article for details
As with basically every exchanger task, I have a producer filling up an empty buffer2, a consumer clearing a full buffer1 and when each thread is done, they should exchange their respective buffers.
I am really unsure about where and how to apply the exchange. I defined readyconsumer and readyproducer as booleans, so that a third thread can check whether it's time to exchange the buffers once both are true. This should solve the problem I had doing it with two threads, where the program was stuck with both threads at wait() (which it unfortunately still is).
This is what the code looks like at the moment. Can anyone help me in which class I have to exchange and at what point in the code? Thank you very much in advance!
class Buffer {
static boolean readyconsumer, readyproducer = false;
volatile int count; // number of put actions
static int max = 10;
Buffer() {
count = 0;
}
public synchronized void put() {
if (count == max) {
readyproducer = true;
System.out.println(" wait ");
try {
wait();
} catch (InterruptedException e) {
}
}
count++;
System.out.println("put " + count);
notifyAll();
}
public synchronized void get() {
if (count == 0) {
readyconsumer = true;
System.out.println(" wait");
try {
wait();
} catch (InterruptedException e) {
}
}
count--;
System.out.println("get " + count);
notifyAll();
}
}
class CheckandSwitch extends ProdCon {
public void run() {
while (true) {
if (Buffer.readyconsumer && Buffer.readyproducer) {
try {
ProdCon.buffer2 = exchanger.exchange(ProdCon.buffer1);
ProdCon.buffer1 = exchanger.exchange(ProdCon.buffer2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Buffer.readyconsumer = false;
Buffer.readyproducer = false;
buffer1.count = 0;
buffer2.count = 10;
notifyAll();
}
}
}
}
class Consumer extends ProdCon {
static Buffer buffer;
Consumer(Buffer b) {
super();
buffer = b;
b.count = 10;
}
public void run() {
while (true) {
consume();
buffer.get();
}
}
private void consume() {
System.out.println("consume");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
}
}
class Producer extends ProdCon {
static Buffer buffer;
Producer(Buffer b) {
super();
buffer = b;
b.count = 0;
}
public void run() {
while (true) {
produce();
buffer.put();
}
}
private void produce() {
System.out.println("produce ");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
}
import java.util.concurrent.*;
public class ProdCon extends Thread {
static Exchanger<Buffer> exchanger = new Exchanger<Buffer>();
static Buffer buffer1, buffer2 = null;
public static void main(String[] args) {
buffer1 = new Buffer();
buffer2 = new Buffer();
new Consumer(buffer1).start();
new Producer(buffer2).start();
new CheckandSwitch().start();
}
}
You could use an Exchanger.
Here's the code from the javadoc tweaked into a working example.
class DataBuffer<T> {
T data = null;
public boolean isFull() {
return data != null;
}
public boolean isEmpty() {
return data == null;
}
public T get() {
T d = data;
data = null;
return d;
}
public void put(T data) {
this.data = data;
}
}
class FillAndEmpty {
Exchanger<DataBuffer<Integer>> exchanger = new Exchanger<>();
DataBuffer<Integer> initialEmptyBuffer = new DataBuffer<>();
DataBuffer<Integer> initialFullBuffer = new DataBuffer<>();
int countDown = 10;
class FillingLoop implements Runnable {
#Override
public void run() {
DataBuffer currentBuffer = initialEmptyBuffer;
try {
while (currentBuffer != null && countDown > 0) {
addToBuffer(currentBuffer);
if (currentBuffer.isFull()) {
currentBuffer = exchanger.exchange(currentBuffer);
}
}
} catch (InterruptedException ex) {
}
}
private void addToBuffer(DataBuffer<Integer> currentBuffer) {
currentBuffer.put(countDown--);
}
}
class EmptyingLoop implements Runnable {
#Override
public void run() {
DataBuffer<Integer> currentBuffer = initialFullBuffer;
try {
while (currentBuffer != null) {
takeFromBuffer(currentBuffer);
if (currentBuffer.isEmpty()) {
currentBuffer = exchanger.exchange(currentBuffer);
}
}
} catch (InterruptedException ex) {
}
}
private void takeFromBuffer(DataBuffer<Integer> currentBuffer) {
System.out.println(currentBuffer.get());
}
}
void start() {
new Thread(new FillingLoop()).start();
new Thread(new EmptyingLoop()).start();
}
}
public void test() {
System.out.println("Hello");
new FillAndEmpty().start();
}
I have a problem when scheduling two threads (ReadData and WriteData). It seems that I can't notify after wait.
Here is my class which I define and call:
Buffer: The buffer I use to read / write data. I synch this class.
public class Buffer {
// Size of buffer we use to store data
public static final int SIZE = 10;
// Data of buffer.
private int[] values;
// Count of element in data.
private int count;
// Instance of buffer, for singleton pattern
private static Buffer instance = null;
// A signal show data in use (busy) or not
private static Object mutex = new Object();
// Constructor of buffer
private Buffer(){
values = new int[SIZE];
count = 0;
}
// Get instance of buffer.
public static Buffer getInstance(){
if(instance == null){
synchronized (mutex) {
if(instance == null) instance = new Buffer();
}
}
return instance;
}
public void addValue(int value){
synchronized (mutex) {
if(count >= SIZE) return;
values[count++] = value;
}
}
// Return current data and then reset buffer.
public int[] getValues(){
synchronized (mutex) {
if(count == 0) return null;
int[] values = new int[count];
System.arraycopy(this.values, 0, values, 0, count);
count = 0;
return values;
}
}
public int getCount(){
synchronized (mutex) {
return count;
}
}
}
ReadData: This class I use to store data into the buffer.
import java.util.Random;
public class ReadData implements Runnable {
Buffer buffer = Buffer.getInstance();
#Override
public synchronized void run() {
Random random = new Random();
while(true){
if(buffer.getCount() >= Buffer.SIZE){
try {
System.out.println("Read: is waiting. . .");
mState.readIsWaiting();
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("Read: storing data");
buffer.addValue(random.nextInt(Buffer.SIZE) + 1);
}
}
}
public interface ReadState{
void readIsWaiting();
}
private ReadState mState;
public void setReadState(ReadState state){
mState = state;
}
public synchronized void makeNotify() {
notifyAll();
}
}
WriteData: This class I use to get data from the buffer
public class WriteData implements Runnable {
Buffer buffer = Buffer.getInstance();
#Override
public synchronized void run() {
while(true){
if(buffer.getCount() == 0){
try {
System.out.println("Write: is waiting. . .");
mState.writeIsWaiting();
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
int[] getdata = buffer.getValues();
System.out.println("first data: " + getdata[0]);
}
}
}
public interface WriteState{
void writeIsWaiting();
}
private WriteState mState;
public void setWriteState(WriteState state){
mState = state;
}
public synchronized void makeNotify() {
notifyAll();
}
}
MyThread: The thread I use to start read/write
package main;
import testing.ReadData;
import testing.WriteData;
public class MyThread implements ReadData.ReadState, WriteData.WriteState {
private ReadData read;
private WriteData write;
public void start(){
read = new ReadData();
write = new WriteData();
read.setReadState(this);
write.setWriteState(this);
new Thread(read).start();
new Thread(write).start();
}
#Override
public void writeIsWaiting() {
read.makeNotify();
}
#Override
public void readIsWaiting() {
write.makeNotify();
}
}
That's it. Sometimes it works, many times it stops and waits.
How do I solve this problem? Thanks
I think you've reversed the ReadData implementation with the WriteData. In the code shown, the ReadData thread will block if the buffer is full:
#Override
public synchronized void run() {
Random random = new Random();
while(true){
if(buffer.getCount() >= Buffer.SIZE){
try {
System.out.println("Read: is waiting. . .");
mState.readIsWaiting();
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("Read: storing data");
buffer.addValue(random.nextInt(Buffer.SIZE) + 1);
}
}
}
What you really need is:
#Override
public synchronized void run() {
Random random = new Random();
while(true){
if(buffer.getCount() == 0){
try {
System.out.println("Read: is waiting. . .");
mState.readIsWaiting();
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("Read: storing data");
buffer.addValue(random.nextInt(Buffer.SIZE) + 1);
}
}
}
Similarly, in the WriteData implementation, you should block if the buffer is full. This will happen if the reader hasn't had a chance to take the elements from the buffer. This should work for the WriteData code:
#Override
public synchronized void run() {
while(true){
if(buffer.getCount() >= Buffer.SIZE){
try {
System.out.println("Write: is waiting. . .");
mState.writeIsWaiting();
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
int[] getdata = buffer.getValues();
System.out.println("first data: " + getdata[0]);
}
}
}