Make threads usage efficient in Java - java

I have coded a simple application in Java that downloads particular images from a list of html links provided. Everything was working fine until I added the feature of having to download from a list of html links rather than just one. I had to implement the wait() and notify() methods which forced me to change the approach a little. Now, the downloads work fine, but the GUI does not update while the download is in progress.
I make the 1st thread wait from HTML.java and notify it at the end of DownloadImages.java. For this I had to invoke buttonPressed class as an object rather than a thread, which is why I think my GUI won't update.
Is there a way to simplify or make thread-usage more efficient in my code? Thanks in advance.
Here is skeleton of my code:
/*Test.java*/
package my;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Test extends javax.swing.JFrame {
public static buttonPressed bp;
public static boolean alldone;
/** Creates new form Test */
public Test() {
initComponents();
}
public static class buttonPressed implements Runnable {
Thread t1, t2;
buttonPressed() {
t1 = new Thread(this, "downloadAction");
t1.start();
}
public void suspendThread() {
System.out.println("suspended");
alldone = false;
}
public synchronized void resumeThread() {
System.out.println("resumed");
alldone = true;
notify();
}
public void run() {
String[] len = new String[]{/*list of urls*/};
for (int i = 0; i &lt len.length; i++) {
System.out.println("going times: " + i);
t2 = new Thread(new HTML(), "HTMLthread");
t2.start();
synchronized (this) {
while (!alldone) {
try {
wait();
} catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}
}
private void downloadActionPerformed(java.awt.event.ActionEvent evt) {
bp = new buttonPressed();
try {
bp.t1.join();
} catch (InterruptedException e) {
System.out.println("Main Thread: interrupted");
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Test().setVisible(true);
}
});
}
private javax.swing.JButton download;
public static javax.swing.JProgressBar progress;
}
/*HTML.java*/
package my;
import java.util.ArrayList;
class HTML implements Runnable {
private Thread t3;
public HTML() {
Test.bp.suspendThread();
}
public void run() {
downloadHTML();
ArrayList xyz = parseHTML();
t3 = new Thread(new DownloadImages(xyz), "DownDecrypt");
t3.start();
}
private void downloadHTML() {
// Downloads the HTML file
}
private ArrayList parseHTML() {
// Parses the HTML file and gets links to images
return new ArrayList();
}
}
/*DownloadImages.java*/
package my;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
class DownloadImages implements Runnable {
static int current = 0, previous = 0;
static boolean speedFlag;
ArrayList<String> links = new ArrayList<String>();
private Thread t4;
public DownloadImages(ArrayList param1) {
this.links = param1;
speedFlag = true;
}
public void run() {
t4 = new Thread(new getSpeed(), "getSpeed");
t4.start();
download(links);
}
private void download(ArrayList<String> param1) {
String[] imgurl = new String[param1.size()];
URLConnection conn = null;
InputStream is = null;
ByteArrayOutputStream bais = null;
int prog;
for (int i = 0; i < param1.size(); i++) {
current = 0;
imgurl[i] = param1.get(i);
try {
conn = new URL(imgurl[i]).openConnection();
int fsize = conn.getContentLength();
is = new BufferedInputStream(conn.getInputStream());
bais = new ByteArrayOutputStream();
byte[] byteChunk = new byte[1024];
int n;
while ((n = is.read(byteChunk)) > 0) {
bais.write(byteChunk, 0, n);
current = current + 1024;
prog = (int) (current * 100.0 / fsize);
Test.progress.setValue(prog);
}
} catch (MalformedURLException ex) {
Logger.getLogger(DownloadImages.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ex) {
Logger.getLogger(DownloadImages.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
byte[] imgBytes = bais.toByteArray();
try {
FileOutputStream fos = new FileOutputStream(i + ".jpg");
fos.write(imgBytes);
fos.flush();
fos.close();
} catch (FileNotFoundException ex) {
System.out.println("FileNotFoundException : " + ex);
} catch (IOException e) {
e.printStackTrace();
}
}
speedFlag = false;
// Resume the thread to start downloading the next link
Test.bp.resumeThread();
}
private static class getSpeed implements Runnable {
int kbytesPerSecond;
private final int fireTime;
public getSpeed() {
fireTime = 1000;
}
public void run() {
while (speedFlag) {
try {
Thread.sleep(fireTime);
} catch (InterruptedException ex) {
Logger.getLogger(getSpeed.class.getName()).log(Level.SEVERE, null, ex);
}
kbytesPerSecond = (((current - previous) / 1024) / (fireTime / 1000));
System.out.println(kbytesPerSecond);
previous = current;
}
}
}
}

As far as the GUI is concerned you need to read about Swing concurrency. In short, use SwingWorker.
Mind that you use old AWT stuff (java.awt.EventQueue).

I suggest you have an ExecutorService like Executors.newCachedThreadPool and submit() the tasks to it. Collect the Future objects so you know when they are done. This will be more efficient and manageable than creating Threads all over the place.
You can have just one pool like
static final ExecutorService POOL = Executors.newCachedThreadPool();
to submit a task
POOL.submit(new Callable<Void>() {
public Void call() throws InterruptedException {
while (speedFlag) {
Thread.sleep(1000);
kbytesPerSecond = (current - previous) / 1024;
System.out.println(kbytesPerSecond);
previous = current;
}
}
});
Even better for repeating tasks is to use a scheduled executor service.
static final ScheduledExecutorService POOL = Executors.newScheduledThreadPool(4);
Future task = POOL.scheduleAtFixedRate(new Runnable() {
public void run() {
kbytesPerSecond = (current - previous) / 1024;
System.out.println(kbytesPerSecond);
previous = current;
}
}, 1, 1, TimeUnit.SECONDS);
// to end the task
task.cancel(false);

Related

ScheduledThreadPoolExecutor scheduled purging memory leak

I am trying to write a enclosed class to download images from the web that will refresh every 30 seconds. I might want to download 1 image or I might want to download N images. And I might want to stop downloading a certain image at any time. I wrote the following class that works great except when I stop downloading an image memory is not being released for that task. Or if I stop all images from being downloaded memory is not released (This won't happen in production). I have tried several different ways to achieve this. My last attempt was to purge the tasks from the ScheduledThreadPoolExecutor every 30 seconds using the same executor or with the code below a separate executor. I am also providing code to test releasing memory, although my example stops all images from being downloaded when in a real life usage I should be able only stop one image and the memory be released from that one task.
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
public class ImageLoadTask implements Runnable {
private static ScheduledThreadPoolExecutor taskExecutorService = new ScheduledThreadPoolExecutor(500);
private static ScheduledThreadPoolExecutor purgeExecutorService = new ScheduledThreadPoolExecutor(500);
private static Runnable purgeRunnable = () -> purge();
private ScheduledFuture<?> scheduledFuture;
private URL pictureURL;
private Consumer<BufferedImage> successMethod;
private Consumer<String> failMethod;
private ImageURLStreamHandler streamHandler = new ImageURLStreamHandler();
private boolean displaySuccess = false;
private boolean displayError = false;
private boolean isCancelled = false;
static {
taskExecutorService.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
taskExecutorService.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
taskExecutorService.setRemoveOnCancelPolicy(true);
purgeExecutorService.scheduleWithFixedDelay(purgeRunnable, 30L, 30L, TimeUnit.SECONDS);
}
public ImageLoadTask(String url) {
try {
this.pictureURL = new URL(url);
} catch (MalformedURLException e) {
if(failMethod != null) {
failMethod.accept(e.getMessage()); ;
}
if(displayError) {
System.out.println("(ImageLoadTask) URL is malformed: " + url+"\n"+ e.getMessage());
}
}
}
public ImageLoadTask(String url, Consumer<BufferedImage> successMethod) {
this(url);
this.successMethod = successMethod;
}
public ImageLoadTask(String url, Consumer<BufferedImage> successMethod, Consumer<String> failMethod) {
this(url, successMethod);
this.failMethod = failMethod;
}
public void start() {
scheduledFuture = taskExecutorService.scheduleAtFixedRate(this, 0L, 30L, TimeUnit.SECONDS);
}
public void stop() {
if(isCancelled)
return;
isCancelled = true;
scheduledFuture.cancel(true);
scheduledFuture = null;
pictureURL = null;
successMethod = null;
failMethod = null;
streamHandler = null;
taskExecutorService.remove(this);
taskExecutorService.purge();
}
public static void purge() {
System.out.println("Purging");
taskExecutorService.purge();
}
#Override
public void run() {
if(!isCancelled) {
try {
BufferedImage image = loadImage(pictureURL);
if(displaySuccess) {
System.out.println("Image received for url " + pictureURL);
}
if(successMethod != null && !isCancelled) {
successMethod.accept(image); ;
}
} catch (IOException e) {
if(failMethod != null && !isCancelled) {
failMethod.accept(e.getMessage());
}
if(displayError) {
System.out.println("Error occured retrieving image for url: " + pictureURL +"\n"+ e.getMessage());
}
}
}
}
public void displayError(boolean displayError) {
this.displayError = displayError;
}
public void displaySuccess(boolean displaySuccess) {
this.displaySuccess = displaySuccess;
}
private BufferedImage loadImage(URL input) throws IOException {
if (input == null) {
throw new IllegalArgumentException("input == null!");
}
InputStream istream = null;
try {
istream = streamHandler.openConnection(input).getInputStream();
} catch (IOException e) {
throw new IIOException("Can't get input stream from URL!", e);
}
ImageInputStream stream = ImageIO.createImageInputStream(istream);
BufferedImage bi;
try {
bi = ImageIO.read(stream);
if (bi == null) {
stream.close();
}
} finally {
istream.close();
}
return bi;
}
#Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize");
}
class ImageURLStreamHandler extends URLStreamHandler {
#Override
protected URLConnection openConnection(URL url) throws IOException {
URL target = new URL(url.toString());
URLConnection connection = target.openConnection();
// Connection settings
connection.setConnectTimeout(60000); // 1 min
connection.setReadTimeout(60000); // 1 min
return connection;
}
}
}
Test App:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageLoadTaskTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Gui gui = new Gui();
}
});
}
static class Gui extends JFrame {
private static final long serialVersionUID = 1L;
private List<ImageLoadTask> tasks = new ArrayList<>();
private boolean running = false;
private JButton startStopButton = new JButton("Start");
private JButton purgeButton = new JButton("Purge");
private ActionListener startStopListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(running) {
stopTasks();
} else {
startTasks();
}
}
};
private ActionListener purgeListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ImageLoadTask.purge();
}
};
public Gui() {
setTitle("Image Load Task Test");
setBounds(250, 250, 300, 150); // Size
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel contentPanel = new JPanel();
setContentPane(contentPanel);
startStopButton.addActionListener(startStopListener);
contentPanel.add(startStopButton);
purgeButton.addActionListener(purgeListener);
contentPanel.add(purgeButton);
setVisible(true);
}
private void startTasks() {
running = true;
System.out.println("Starting tasks");
for(int i = 0; i < 2500; i++) {
ImageLoadTask task = new ImageLoadTask("http://placehold.it/120x120&text=image" + i, this::success, this::fail);
task.start();
tasks.add(task);
}
startStopButton.setText("Stop");
}
private void stopTasks() {
running = false;
System.out.println("Stopping " + tasks.size() + " tasks");
for(ImageLoadTask task : tasks) {
task.stop();
}
tasks.clear();
startStopButton.setText("Start");
System.out.println("Stopped tasks ");
//ImageLoadTask.purge();
}
private void success(BufferedImage image) {
//System.out.println("Success!");
}
private void fail(String message) {
//System.out.println("Fail! "+ message);
}
}
}
You don't close your 'stream' when you interrupt ImageIO.read(stream).

Following method call prevents JLabel from getting visible [duplicate]

I'm writing a program that constantly pings a server. I wrote the code to check it once and put the ping in a JLabel and put it in a method called setPing().
Here is my code
private void formWindowOpened(java.awt.event.WindowEvent evt) {
setPing();
}
That worked but only did it once, so I did:
private void formWindowOpened(java.awt.event.WindowEvent evt) {
for(;;){
setPing();
}
}
But this doesn't even work for the first time.
I didnt put the setPing method because it was too long so here it is:
public String setPing(){
Runtime runtime = Runtime.getRuntime();
try{
Process process = runtime.exec("ping lol.garena.com");
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
int i = 0;
i = line.indexOf("Average");
if(i > 0){
String finalPing = "";
line.toCharArray();
try
{
finalPing = "";
for(int x = i; x < i + 17; x++)
{
finalPing = finalPing + (line.charAt(x));
}
}catch(IndexOutOfBoundsException e)
{
try
{
finalPing = "";
for(int x = i; x < i + 16; x++)
{
finalPing = finalPing + (line.charAt(x));
}
}catch(IndexOutOfBoundsException f)
{
try
{
finalPing = "";
for(int x = i; x < i + 15; x++)
{
finalPing = finalPing + (line.charAt(x));
}
}catch(IndexOutOfBoundsException g){}
}
}
String final1Ping = finalPing.replaceAll("[^0-9]", "");
return final1Ping;
}
}
}catch(IOException e){
}
return "";
}
UPDATE
Just in case this is important, Im using netbeans. I created a form and put this code in the formWindowOpened evt instead of calling it in main:
private void formWindowOpened(java.awt.event.WindowEvent evt) {
ActionListener timerListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new PingWorker().execute();
}
};
Timer timer = new Timer(1000, timerListener);
timer.start();
jLabel1.setText(label.getText());
timer.stop();
// TODO add your handling code here:
}
class PingWorker extends SwingWorker {
int time;
#Override
protected Object doInBackground() throws Exception {
time = pingTime("lol.garena.com");
return new Integer(time);
}
#Override
protected void done() {
label.setText("" + time);
}
};
public JComponent getUI() {
return label;
}
public static int pingTime(String hostnameOrIP) {
Socket socket = null;
long start = System.currentTimeMillis();
try {
socket = new Socket(hostnameOrIP, 80);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
}
long end = System.currentTimeMillis();
return (int) (end - start);
}
Use a Swing Timer for repeating tasks & a SwingWorker for long running tasks. E.G. of both below - it uses a Timer to repeatedly perform a 'long running' task (a ping) in a SwingWorker.
See Concurrency in Swing for more details on the Event Dispatch Thread and doing long running or repeating tasks in a GUI.
This code combines a long running task ('pinging' a server) using SwingWorker invoked from a repeating task (updating the JLabel repeatedly with the times) using a Swing based Timer.
import java.awt.event.*;
import javax.swing.*;
import java.net.Socket;
public class LabelUpdateUsingTimer {
static String hostnameOrIP = "stackoverflow.com";
int delay = 5000;
JLabel label = new JLabel("0000");
LabelUpdateUsingTimer() {
label.setFont(label.getFont().deriveFont(120f));
ActionListener timerListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new PingWorker().execute();
}
};
Timer timer = new Timer(delay, timerListener);
timer.start();
JOptionPane.showMessageDialog(
null, label, hostnameOrIP, JOptionPane.INFORMATION_MESSAGE);
timer.stop();
}
class PingWorker extends SwingWorker {
int time;
#Override
protected Object doInBackground() throws Exception {
time = pingTime();
return new Integer(time);
}
#Override
protected void done() {
label.setText("" + time);
}
};
public static int pingTime() {
Socket socket = null;
long start = System.currentTimeMillis();
try {
socket = new Socket(hostnameOrIP, 80);
} catch (Exception weTried) {
} finally {
if (socket != null) {
try {
socket.close();
} catch (Exception weTried) {}
}
}
long end = System.currentTimeMillis();
return (int) (end - start);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new LabelUpdateUsingTimer();
}
};
SwingUtilities.invokeLater(r);
}
}
You could use a Thread. The problem is you are blocking the main thread, thereby blocking your program. To get around this, start a background Thread to update components repeatedly.
(Note: you need to update GUI components on the EDT, so use SwingUtilities.invokeLater)
(new Thread((new Runnable(){
#Override
public void run(){
while(true){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
refToJLabel.setText(Math.random());
}
});
}
}
}))).start();

Can I combine this into one loop? [duplicate]

This question already has answers here:
How to chain multiple different InputStreams into one InputStream
(5 answers)
Closed 8 years ago.
Hi i have a run method thats inside a Thread that writes either using a fileOutputStream stream or a RandomaccessFile. I have it in 2 while loops I'de like to combine it into 1 but I'm lost as to how. here is the run method.
package treadmakestop;
import java.awt.EventQueue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
/**
*
* #author brett
*/
public class Worker implements Runnable {
private final ReentrantLock PAUSELOCK;
private final Condition PAUSECONDITION;
private final AtomicBoolean PAUSED;
private final AtomicBoolean KEEPRUNNING;
private final AtomicBoolean KILLSTREAM;
private final File FILEIN = new File("C:\\Users\\brett\\Documents\\Back to the Beginning Origins Nova Neil Degrasse Tyson Documentary-D-EXw5CdPtM.mp4");
private final File WORKINGDIR = new File(System.getProperty("user.home") + File.separator + "Documents" + File.separator + "ConvertedTvFiles" + File.separator);
private final File FILEOUT = new File(WORKINGDIR + File.separator + "spew" + ".mp4");
private final JProgressBar TVPG;
private final long LENGTH = FILEIN.length();
private final byte[] b = new byte[1024];
private FileOutputStream FOUTS;
private FileInputStream FINS;
private RandomAccessFile RandFileOut;
private RandomAccessFile RandFileIn;
private int r;
private long Counter = 1;
public Worker(JProgressBar tvpg) {
PAUSED = new AtomicBoolean();
KEEPRUNNING = new AtomicBoolean(true);
KILLSTREAM = new AtomicBoolean(false);
PAUSELOCK = new ReentrantLock();
PAUSECONDITION = PAUSELOCK.newCondition();
TVPG = tvpg;
}
/**
* This is the main thread and copy file operation
*/
#Override
public void run() {
System.out.println("Runnable has started");
int progress;
try {
createFirstFileStream();
while ((r = FINS.read(b)) != -1 && KEEPRUNNING.get()) {
checkPauseState();
if (!KILLSTREAM.get()) {
Counter += r;
FOUTS.write(b, 0, r);
progress = (int) Math.round(100 * Counter / LENGTH);
updateProgress(Math.min(progress, 100));
} else {
killFileStream();
break;
}
}
createRandomFile();
while ((r = RandFileIn.read(b)) != -1 && KEEPRUNNING.get()) {
checkPauseState();
if (KILLSTREAM.get()) {
Counter += r;
RandFileOut.write(b, 0, r);
progress = (int) Math.round(100 * Counter / LENGTH);
updateProgress(Math.min(progress, 100));
} else {
killRandFile();
break;
}
}
} catch (IOException ex) {
Logger.getLogger(Worker.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Runnable has exited");
}
public void createFirstFileStream() throws IOException {
FINS = new FileInputStream(FILEIN);
FOUTS = new FileOutputStream(FILEOUT);
}
public void createRandomFile() throws IOException {
RandFileIn = new RandomAccessFile(FILEIN, "rw");
RandFileIn.seek(FILEOUT.length());
long pointer = RandFileIn.getFilePointer();
RandFileOut = new RandomAccessFile(FILEOUT, "rw");
RandFileOut.seek(pointer);
}
public boolean isPaused() {
return PAUSED.get();
}
public void pause() {
KILLSTREAM.set(true);
PAUSED.set(true);
}
public void resume() {
PAUSED.set(false);
PAUSELOCK.lock();
try {
PAUSECONDITION.signal();
} finally {
PAUSELOCK.unlock();
}
}
protected void checkPauseState() {
while (PAUSED.get()) {
PAUSELOCK.lock();
try {
PAUSECONDITION.await();
} catch (Exception e) {
} finally {
PAUSELOCK.unlock();
}
}
}
protected void updateProgress(int progress) {
if (EventQueue.isDispatchThread()) {
TVPG.setValue(progress);
if (KEEPRUNNING.get() == false) {
TVPG.setValue(0);
}
} else {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
updateProgress(progress);
}
});
}
}
public void killFileStream() throws IOException {
FINS.close();
FOUTS.flush();
FOUTS.close();
}
public void killRandFile() throws IOException {
RandFileIn.close();
RandFileOut.close();
}
public synchronized void stop() {
KEEPRUNNING.set(false);
resume();
}
}
Here is the whole class
Something like this should work:
createFirstFileStream();
createRandomFile();
InputStream combined = new SequentialInputStream(FINS,
Channels.createInputStream(RandomFileIn.getChannel());
// your while loop
while ((r = combined.read(b)) != -1 && KEEPRUNNING.get()) {
if (!KILLSTREAM.get()) {
Counter += r;
FOUTS.write(b, 0, r);
progress = (int) Math.round(100 * Counter / LENGTH);
updateProgress(Math.min(progress, 100));
} else {
killFileStream();
break;
}
}
Alternatively, wrap the loop in a function and call it twice, using the Channels call above to wrap the RandomAccessFile to an InputStream.

Trying to solve consumer-producer in java with multithreading

I'm trying to solve the producer consumer problem with threads in java, but the code won't run in parallell/concurrently. The producer always fills up the buffer completely before the consumer starts to consume, and I don't get why. The point is trying to do it using only synchronized blocks, wait() and notify().
Main :
String [] data = {"Fisk", "Katt", "Hund", "Sau", "Fugl", "Elg", "Tiger",
"Kameleon", "Isbjørn", "Puma"};
ProducerConsumer pc = new ProducerConsumer(5);
Thread[] thrds = new Thread[2];
thrds[0] = new Thread(new MyThread1(pc, data)); // producer
thrds[1] = new Thread(new MyThread2(pc)); // consumer
thrds[0].start();
thrds[1].start();
for(int i = 0; i < 2; i++) { // wait for all threads to die
try {
thrds[i].join();
}
catch (InterruptedException ie) {}
}
System.exit(0);
ProducerConsumer.java:
import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumer implements Runnable {
private int bufferSize;
private Queue<String> buffer;
public ProducerConsumer(int size) {
bufferSize = size;
buffer = new LinkedList<String>();
}
public void produce(String item) throws InterruptedException {
synchronized(buffer) {
while (buffer.size() >= bufferSize) {
try {
System.out.println("Full buffer. Waiting for consumer...");
buffer.wait();
}catch (Exception e) {}
}
buffer.add(item);
System.out.println("Producer is putting " + item + " in the buffer");
buffer.notify();
}
}
public void consume() throws InterruptedException {
synchronized (buffer) {
while (buffer.size() == 0) {
try {
System.out.println("Empty buffer. Waiting for production...");
buffer.wait();
}catch (Exception e) {}
}
System.out.println("Consumer is consuming " + buffer.remove() + ".");
buffer.notify();
}
}
#Override
public void run() {
}
}
MyThread1 :
/*
* PRODUCER - Thread
*/
public class MyThread1 implements Runnable {
private String [] data;
private ProducerConsumer pc;
public MyThread1(ProducerConsumer pc, String [] data) {
this.pc = pc;
this.data = data;
}
#Override
public void run() {
for (int i = 0; i < data.length; i++) {
try {
pc.produce(data[i]);
} catch (InterruptedException ex) {}
}
}
}
MyThread2:
//THE CONSUMER - Thread
public class MyThread2 implements Runnable{
private ProducerConsumer pc;
public MyThread2(ProducerConsumer pc) {
this.pc = pc;
}
//Run consume
#Override
public void run() {
while (true) {
try {
pc.consume();
Thread.sleep(2);
}
catch(InterruptedException e) {}
}
}
}
On recent machines, with short queues like this, you will never see actual multithreading effects like, in this case, producer and consumer taking turns unless you slow both of them down a bit. You only slowed down the consumer. Instead of using a short array, put a million Integers in a queue and see what happens.
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class ProduserConsumerDemo {
public static void main(String[] args) {
List<Integer> list = new CopyOnWriteArrayList<>();
int size = 5;
Producer producer = new Producer(list, size);
Consumer consumer = new Consumer(list);
Thread t1 = new Thread(producer, "Producer");
Thread t2 = new Thread(consumer, "Consumer");
t1.start();
t2.start();
}
}
class Producer implements Runnable {
private final List<Integer> list;
private final int size;
public Producer(List<Integer> list, final int size) {
this.list = list;
this.size = size;
}
public void run() {
try {
produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void produce() throws InterruptedException {
int i = 0;
while (i >= 0) {
synchronized (list) {
while (list.size() == size) {
System.out.println(
"List is full." + Thread.currentThread().getName() + " is waiting. Size:" + list.size());
list.wait();
}
System.out.println("Produce :" + i);
list.add(i++);
Thread.sleep(50);
list.notify();
}
}
}
}
class Consumer implements Runnable {
private final List<Integer> list;
public Consumer(List<Integer> list) {
this.list = list;
}
public void run() {
try {
consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void consume() throws InterruptedException {
while (true) {
synchronized (list) {
while (list.isEmpty()) {
System.out.println(
"List is empty. " + Thread.currentThread().getName() + " is waiting. Size:" + list.size());
list.wait();
}
System.out.println("Consumed item:" + list.remove(0));
Thread.sleep(50);
list.notify();
}
}
}
}

Restrict multiple instances of an application in java

I want to prevent multiple instances of application being launched in java. I know 2 methods for this:
locking file
locking socket
But which is one is more efficient and good to use? Which one should I use?
Any other solution to do the same are also welcome.
There is a library called jUnique which does that and will save you the bother of implementing it yourself.
If you deploy with Java WebStart the SingleInstanceService does this.
See http://download.oracle.com/javase/6/docs/technotes/guides/javaws/developersguide/faq.html#218
EDIT: I tried that with Win200864b(version isn't important) and alive JFrame and move toFront() or Iconified in SystemTray with JFrame.DO_NOTHING_ON_CLOSE
public interface ApplicationStartedListener {
void applicationStarted();
void foreignApplicationStarted(String name);
void messageArrived(Object obj);
}
//
import java.io.Serializable;
public class ClassCheck implements Serializable {
private static final long serialVersionUID = 1L;
private String className = null;
public ClassCheck() {
}
public ClassCheck(String className) {
setClassName(className);
}
#Override
public String toString() {
return this.className;
}
public String getClassName() {
return this.className;
}
public void setClassName(String className) {
this.className = className;
}
}
//
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.io.File;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class RunOnceFromFile {
private SingleInstanceController sic = null;
private JFrame frame;
private Robot r;
private JTextField tf;
public RunOnceFromFile() {
try {
r = new Robot();
} catch (AWTException ex) {
ex.printStackTrace();
}
sic = new SingleInstanceController(new File(System.getProperty("java.io.tmpdir") + "Example.file"), "sic_example_application");
if (sic.isOtherInstanceRunning()) {
sic.sendMessageToRunningApplication("toFront");
System.exit(0);
} else {
frame = new JFrame("TEST");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
tf = new JTextField("JTextFiled");
frame.add(tf, BorderLayout.NORTH);
frame.setExtendedState(Frame.ICONIFIED);
frame.setExtendedState(Frame.NORMAL);
frame.setExtendedState(frame.getExtendedState() | JFrame.ICONIFIED);
frame.setExtendedState(frame.getExtendedState() & (~JFrame.ICONIFIED));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
sic.registerApplication();
sic.addApplicationStartedListener(new ApplicationStartedListener() {
public void applicationStarted() {
Runnable doRun = new Runnable() {
public void run() {
frame.toFront();
}
};
SwingUtilities.invokeLater(doRun);
}
public void foreignApplicationStarted(final String name) {
Runnable doRun = new Runnable() {
public void run() {
frame.toFront();
}
};
SwingUtilities.invokeLater(doRun);
}
public void messageArrived(final Object obj) {
Runnable doRun = new Runnable() {//activateWindow(frame);
public void run() {
frame.toFront();
}
};
SwingUtilities.invokeLater(doRun);
}
private void activateWindow(JFrame frame) {
frame.setExtendedState(Frame.ICONIFIED);
frame.setExtendedState(Frame.NORMAL);
frame.setAlwaysOnTop(true);
frame.setAlwaysOnTop(false);
Point location = MouseInfo.getPointerInfo().getLocation();
Point locationOnScreen = frame.getLocationOnScreen();
r.mouseMove(locationOnScreen.x + 100, locationOnScreen.y + 10);
r.mousePress(InputEvent.BUTTON1_MASK);
r.mouseRelease(InputEvent.BUTTON1_MASK);
r.mouseMove(location.x, location.y);
}
});
}
}
public static void main(String[] args) {
RunOnceFromFile roff = new RunOnceFromFile();
}
}
//
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class SingleInstanceController {
private String appname = null;
private Socket client = null;
private File file = null;
private ArrayList<ApplicationStartedListener> listener = null;
private ObjectInputStream ois = null;
private ObjectOutputStream oos = null;
private boolean result = false;
private ServerSocket server = null;
public SingleInstanceController(String appname) {
this(new File(System.getProperty("java.io.tmpdir") + "/923jhakE53Kk9235b43.6m7"), appname);
}
public SingleInstanceController(File file, String appname) {
this.file = file;
this.appname = appname;
this.listener = new ArrayList<ApplicationStartedListener>();
}
public void addApplicationStartedListener(ApplicationStartedListener asl) {
this.listener.add(asl);
}
public void removeApplicationStartedListener(ApplicationStartedListener asl) {
this.listener.remove(asl);
}
public boolean isOtherInstanceRunning() {
if (!this.file.exists()) {
return false;
}
return sendMessageToRunningApplication(new ClassCheck(this.appname));
}
public boolean sendMessageToRunningApplication(final Object obj) {
this.result = false;
try {
this.client = new Socket("localhost", getPortNumber());
new Thread(new Runnable() {
public void run() {
try {
SingleInstanceController.this.oos = new ObjectOutputStream(SingleInstanceController.this.client.getOutputStream());
SingleInstanceController.this.ois = new ObjectInputStream(SingleInstanceController.this.client.getInputStream());
SingleInstanceController.this.oos.writeObject(obj);
SingleInstanceController.this.oos.flush();
SingleInstanceController.this.result = SingleInstanceController.this.ois.readBoolean();
} catch (IOException e) {
SingleInstanceController.this.result = false;
}
}
}).start();
for (int i = 0; i < 10; i++) {
if (this.result == true) {
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.client.close();
return this.result;
} catch (IOException e) {
return false;
}
}
public boolean registerApplication() {
try {
if (!this.file.exists()) {
if (!this.file.getParentFile().mkdirs() && !this.file.getParentFile().exists()) {
return false;
}
if (!this.file.createNewFile()) {
return false;
}
}
BufferedWriter wuffy = new BufferedWriter(new FileWriter(this.file));
int port = getFreeServerSocket();
if (port != -1) {
startServer();
}
wuffy.write(String.valueOf(port));
wuffy.close();
return true;
} catch (IOException e) {
return false;
}
}
protected void messageArrived(Object obj) {
for (ApplicationStartedListener asl : this.listener) {
asl.messageArrived(obj);
}
}
protected void applicationStartet() {
for (ApplicationStartedListener asl : this.listener) {
asl.applicationStarted();
}
}
protected void foreignApplicationStarted(String name) {
for (ApplicationStartedListener asl : this.listener) {
asl.foreignApplicationStarted(name);
}
}
private int getPortNumber() {
try {
BufferedReader buffy = new BufferedReader(new FileReader(this.file));
int port = Integer.parseInt(buffy.readLine().trim());
buffy.close();
return port;
} catch (Exception e) {
return -1;
}
}
private void startServer() {
new Thread(new Runnable() {
public void run() {
while (true) {
try {
SingleInstanceController.this.client = SingleInstanceController.this.server.accept();
if (SingleInstanceController.this.client.getInetAddress().isLoopbackAddress()) {
new Thread(new Runnable() {
public void run() {
try {
SingleInstanceController.this.oos = new ObjectOutputStream(SingleInstanceController.this.client.getOutputStream());
SingleInstanceController.this.ois = new ObjectInputStream(SingleInstanceController.this.client.getInputStream());
Object obj = SingleInstanceController.this.ois.readObject();
if (obj instanceof ClassCheck) {
if (obj.toString().equals(SingleInstanceController.this.appname)) {
SingleInstanceController.this.oos.writeBoolean(true);
applicationStartet();
} else {
SingleInstanceController.this.oos.writeBoolean(false);
foreignApplicationStarted(obj.toString());
}
} else {
messageArrived(obj);
SingleInstanceController.this.oos.writeBoolean(true);
}
SingleInstanceController.this.oos.flush();
SingleInstanceController.this.client.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
private int getFreeServerSocket() {
for (int i = 2000; i < 10000; i++) {
try {
this.server = new ServerSocket(i);
return i;
} catch (IOException ignore) {
}
}
return -1;
}
}
My vote goes to locking on a port (i think this is what you mean by socket). I don't know the exact reason for this. But in fact i come across only this as a solution in most practical projects. Though i will be happy to hear the alternative ways.
In response to your question, the port solution will keep more resources from the machine:
- You will keep a port locked: ports are limited and you may find problems with firewalls or other programs listening on the same port.
- You'll need an active thread.
The file solution will use less resources from the machine, to avoid locking the file forever you need to add a thread, to delete the file, in the addShutdownHook method from Runtime.
the serversocket solution is cross-platform . And will not be vulnerable to the program crashing and not resetting the lock.
File lock is better way to do- imo. When you create the file in User's Home directory, this will still work in a multi-user environment.
I came across - JUnique - haven't had a chance to use it
http://www.sauronsoftware.it/projects/junique/manual.php
I know that this question is pretty old, but I have to solve the same problem at the moment. I prefer the socket solution because I have never thought that such kind of task should have anything to do with the file system. It is better to solve the problem in memory and not in the file system I think.

Categories