Jump to content

Java Threads

- - - - -

  • Please log in to reply
6 replies to this topic

#1
wim DC

wim DC

    Writes binary right handed and hex left handed

  • Members
  • PipPipPipPipPipPipPipPipPip
  • 2,084 posts
  • Programming Language:Java, JavaScript, PL/SQL
  • Learning:Java
Threads
Pink text means you can run this example from the attached project. It tells the package you should run.
This looks like a lot of text, but it's actually more code than text. All the code is attached so you can just open it in your IDE and run.

I know there allready is a tutorial about it (->http://forum.codecal...va-threads.html), but I would like to go a bit more in depth. (Read the tutorial tho, it’s short and gets you the basics of how to create a Thread. I will not cover this)
When do you need threads.
Like said in the other thread:

Quote

The first thing is, that the overhead involved for the JVM is less for starting a new Thread compared to a new process.
Second is, in case you put a thread to sleep, the resources are freed and other threads can use it.
So what you have is a time sharing system that splits the time between various threads and you get maximum possible output.
I must partially disagree with the second statement. When a thread locks an object -more on locking further on - it will keep the lock whether it goes to sleep or not.
When will you first need a Thread?
Possibly in situations where you got a GUI and you’re doing some time-taking processing upon clicking a button and you want to display the progress.
Example:
import javax.swing.*;
import java.awt.*;
 
public class Gui extends JFrame {
    private JProgressBar progressBar;
    private JButton button;
 
    public Gui(String title) throws HeadlessException {
        super(title);
 
        progressBar = new JProgressBar(0,100);
        button = new JButton("start");
 
        button.addActionListener(new ButtonListener(this));
 
        add(progressBar);
        add(button, BorderLayout.SOUTH);
 
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(800,600);
        setVisible(true);
    }    
 
    public void setProgress(int progress) {
        progressBar.setValue(progress);
        progressBar.setString(progress + "%");
    }
   
    public static void main(String[] args) {
        Gui gui = new Gui("progress frame");
    }
}
I’m not going to explain the code of the GUI, it’s pretty straightforward and this thread (post) is not a GUI tutorial.
And as ButtonListener:
import be.thread.gui.Gui;
import be.thread.model.Timer;
 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
public class ButtonListener implements ActionListener{
    private Gui gui;
 
    public ButtonListener(Gui gui) {
        this.gui = gui;
    }
 
    public void actionPerformed(ActionEvent e) {
        int i=0;
        while(i<100){
            gui.setProgress(i++);
            gui.repaint();
            System.out.println(i);
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
}
So basically, what it does is: upon clicking the button. A counter starts going up, it changes the value of the progressbar, repaints the gui and sleeps a short while.
The value is also printed to the console. If you run this program (gui.before) you’ll notice the GUI has stopped repainting itself.
Why?
Because you can see the GUI as running in 1 thread. When you’re in the ActionPerformed of the Button, you’re in the “Gui-thread”, since the Gui only uses 1 thread, it can’t go repaint itself, cause the thread is busy in a while(i<100) loop.
You’ll notice that after clicking the button, the GUI freezes (The button even stays clicked), and when the counter reached 100 and leaves the while loop, the GUI updates itself, progress bar jumps from 0 to 100 and the button gets normal again.

This is one of the situations where you just NEED another thread. There’s no way you can get the required behavior without.
Solving this would require creating a thread. I did this by creating a class which implements the Runnable interface:
public class Timer implements Runnable{
    private Gui gui;
 
    public Timer(Gui gui) {
        this.gui = gui;
    }
 
    public void run() {
        int i=0;
        while(i<100){
            gui.setProgress(i++);
            gui.repaint();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
The code is pretty much the same as was originally behind the button. The button code is now changed to:
public class ButtonListener implements ActionListener{
    private Timer timer;
 
    public ButtonListener(Gui gui) {
        timer = new Timer(gui);
    }
 
    public void actionPerformed(ActionEvent e) {
        new Thread(timer).start();
    }
}
There’s really no difference except the fact that it will now run in a separate Thread. Run again (gui.after), and you see the progress bar filling up nicely.

Following the conversation below, here's how it should be done correctly for Swing:
import javax.swing.*;
import java.util.List;


public class Timer extends SwingWorker<Void, Integer>{
    private Gui gui;


    public Timer(Gui gui) {
        this.gui = gui;
    }


    @Override
    protected Void doInBackground() throws Exception {
        int i=0;
        while(i<100){
            publish(i++);
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }


    @Override
    protected void process(List<Integer> chunks) {
        gui.setProgress(chunks.get(chunks.size()-1));
        gui.repaint();
    }
}
And buttonListener changes only 1 line:
public void actionPerformed(ActionEvent e) {
        timer.execute();
}
When else do you need Threads?
Not so often you “Need” threads, you however “want” threads in certain cases to speed things up. Imagine if you were to download a hundred webpages from the internet. So you create an array (or collection) with the urls, you loop through it and start downloading. Problem is: your program will be waiting for the first download to finish before starting the next. There would likely be a dramatic speed increase if every loop you create a thread which will take care of the downloading.


Synchronizing
These were examples where you yourself create the Threads. It’s very likely you’ll have to “think with threads” even though you’re not creating them. For example: Web projects / web services / web something. Because multiple users can go to the same url and access your software, the web container will handle each user in a different thread. So when 2 users simultaneously browse to some url which is mapped to a method, that method can and will be accessed by 2 threads at the same time.
In this case you never wrote a class which implemented the Runnable interface nor did you create a Thread object, yet you’re dealing with it.
Why does this matter?
Example (The good ol’ bank transfer example)
 public class Account {
    private int money;
   
    public void withdraw(int amount){
        if(money >= amount){
            money -= amount;
        }
    }
}
 
Simple class, simple code. This code prevents the money of the account to go below 0, because you first check whether you got enough money before lowering the amount.
Problem: Multiple threads can run this method at the same time.
Your thread isn’t guaranteed to complete the whole withdraw method before the other thread is allowed to start this.
Let’s modify the account to access this with 2 threads:
 public class Account {
    private int money;
 
    public Account(int money) {
        this.money = money;
    }
 
    public void withdraw(int amount) throws InterruptedException {
        if(money >= amount){
            Thread.sleep(1);
            money -= amount;
        }
    }
 
    public int getMoney() {
        return money;
    }
 
    public static void main(String[] args) {
        final Account account = new Account(100);
 
        Runnable runnable = new Runnable() {
            public void run() {
                while(true){
                    try {
                        account.withdraw(70);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(account.getMoney() < 0){
                        System.out.println("short on money: " + account.getMoney());
                        break;
                    }
                }
            }
        };
        Thread thread = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
 
        thread.start();
        thread2.start();
    }
}
 
(bank._1)

Quote

Yea, but you do a Thread.sleep(1); there
Yes, but that’s only to speed up the process so you don’t have to wait 2 days before seeing some result. Fact is: when threads run and when they don’t isn’t determined by Java, the OS / cpu software chooses which thread can run for how long.
It’s likely that at the line of Thread.sleep(1);, even if the line wasn’t there, the OS would decide for that thread to stop running and give the other thread a go. This results in data getting corrupt and these bugs are incredibly hard to find and solve because they occur ‘sometimes’.

Solution: Synchronizing.
Synchronizing, in Java, actually means locking an object. In this case you don’t want thread2 to access the withdraw method as long as thread1 is using it. So you should put the ‘synchronized’ keyword between “public” and “void’ -> public synchronized void withdraw.
Go ahead and run again.(bank._2 -- Runs forever, manually kill the process) It will run forever with no problems.

Now what did you actually do?

Quote

I locked the method.
No! You locked the whole instance of the class.

This example illustrates it:
 public class Account {
    private int money;
 
    public Account(int money) {
        this.money = money;
    }
 
    public synchronized void withdraw(int amount) throws InterruptedException {
        System.out.println("account.withdraw");
        Thread.sleep(10000);
    }
 
    public synchronized void withdraw2(int amount) throws InterruptedException {
        System.out.println("account.withdraw2");
    }
    public static void main(String[] args) throws InterruptedException {
        final Account account = new Account(100);
 
        Runnable runnable = new Runnable() {
            public void run() {
                try {
                    account.withdraw(70);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
 
        Runnable runnable2 = new Runnable() {
            public void run() {
                try {
                    account.withdraw2(70);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
 
        Thread thread = new Thread(runnable);
        Thread thread2 = new Thread(runnable2);
 
        thread.start();
        Thread.sleep(10);
        thread2.start();
    }
}
 
(bank._3)
(note how I added a Thread.sleep(10) before starting the 2nd thread to make sure thread 1 actually gets run first)
The 2nd thread tries to run a totally different synchronized method(withdraw2). Yet it won’t enter it until thread 1 has stopped sleeping after 10 seconds in withdraw.
That’s because thread 1 locked the instance, not just the method.

Quote

that’s pretty stupid. What if 2 methods use different objects, so the 2 methods can run simultaneously, but not the same method twice? Meaning withdraw() can run together with withdraw2(), but not with withdraw().
Then you should not synchronize the method, but the object which is causing the trouble.
We’re going one step back to our withdrawing problem, back to only 1 withdraw method. And 2 thread accessing it. But instead the money is synchronized:

 public class Account {
    private Integer money;
 
    public Account(int money) {
        this.money = money;
    }
 
    public void withdraw(int amount) throws InterruptedException {
         System.out.println(Thread.currentThread().getName() + " entered account.withdraw()");
        synchronized (money){
            System.out.println(Thread.currentThread().getName() + " locked money");
            Thread.sleep(1);
            if(money >= amount){
                money -= amount;
            }
            System.out.println(Thread.currentThread().getName() + " about to unlock money");
        }
    }
 
    public int getMoney() {
        return money;
    }
 
    public static void main(String[] args) {
        final Account account = new Account(100);
 
        Runnable runnable = new Runnable() {
            public void run() {
                while(true){
                    try {
                        account.withdraw(70);
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(account.getMoney() < 0){
                        System.out.println("short on money: " + account.getMoney());
                        break;
                    }
                }
            }
        };
        Thread thread = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        thread.setName("thread1");
        thread2.setName("thread2");
 
        thread.start();
        thread2.start();
    }
}
 
(bank._4)
(Note how I turned money into Integer instead of int, because synchronized locks Objects, not primitives! I also named the threads used in printlns)
Same result as the previous synchronizing solution, but now the whole instance isn’t locked, but the money. Note how the other thread can already enter withdraw(), but waits for the lock.
Output:
….
thread2 locked money
thread1 entered account.withdraw()
thread2 about to unlock money
thread1 locked money
thread1 about to unlock money
 

Note that even when the thread goes to sleep using Thread.sleep() while inside a synchronized{} block, the lock will remain.

Something else about synchronizing Objects: Threads don’t look whether an object is locked, unless you tell them to do so by using synchronized.
To demonstrate this, let’s bring withdraw2 back, but withdraw2 doesn't synchronize the money object:
 public class Account {
    private Integer money;
 
    public Account(int money) {
        this.money = money;
    }
 
    public void withdraw(int amount) throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + " entered account.withdraw()");
        synchronized (money) {
            System.out.println(Thread.currentThread().getName() + " locked money and going to sleep");
            Thread.sleep(5000);
            if (money >= amount) {
                money -= amount;
            }
            System.out.println(Thread.currentThread().getName() + " about to unlock money");
        }
    }
 
    public void withdraw2(int amount) throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + " entered account.withdraw()");
 
        if (money >= amount) {
            money -= amount;
        }
        System.out.println(Thread.currentThread().getName() + " ends account.withdraw()");
 
    }
 
    public int getMoney() {
        return money;
    }
 
    public static void main(String[] args) throws InterruptedException {
        final Account account = new Account(100);
 
        Runnable runnable = new Runnable() {
            public void run() {
                try {
                    account.withdraw(70);
 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
 
        Runnable runnable2 = new Runnable() {
            public void run() {
                try {
                    account.withdraw2(70);
 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread thread = new Thread(runnable);
        Thread thread2 = new Thread(runnable2);
        thread.setName("thread1");
        thread2.setName("thread2");
 
        thread.start();
        Thread.sleep(10);
        thread2.start();
    }
}
 
(bank._5)
Thread 1 goes to sleep in withdraw, while having the lock on “money”. Thread 2 then enters withdraw2, using money in there. But not waiting for thread1! Threads will only check for the lock if you tell them to by using synchronized(object){}!

1 more thing: synchronized locks the object, not the variable referencing (pointing to) it.
Integer a = 1;
Thread 1 locks "a" (actually locks the '1');
a = 2;
Thread 2 can now go into syncronized(a), even though thread 1 still holds the lock on "a". That's because thread1 locked "1", and thread 2 now locked "2".

More to come soon, propably in another thread (post) to not make this one even more lengthier.

Attached Files


Edited by wim DC, 04 September 2011 - 02:19 AM.


#2
farrell2k

farrell2k

    Learning Programmer

  • Members
  • PipPipPip
  • 60 posts
Nice tutorial. Just two quick things, however.

1. JProgressBar's setValue() is not thread safe, so you should definitely not call if from any thread other than the event dispatch thread. Doing so defeats the entire purpose of your tutorial. You create a thread with a while loop that calls a custom setProgress() that calls setValue() and setString(). To be completely thread safe, you need to make sure these two methods are called on the EDT using either a SwingWorker and its progress() or via SwingUtilities.InvokeLater(). SwingWorker obviously makes more sense.

2. When you've called setSize() or even pack() on a JFrame, its components are realized, so any further gui calls such as setVisible() are technically not thread safe. While it is very unlikely that you'll get a repaint request before setVisible() returns, you still should be prepared for it by creating your Gui on the EDT with SwingUtilities.InvokeLater(new GUI()) in your main().

#3
wim DC

wim DC

    Writes binary right handed and hex left handed

  • Members
  • PipPipPipPipPipPipPipPipPip
  • 2,084 posts
  • Programming Language:Java, JavaScript, PL/SQL
  • Learning:Java
Why should I care about number 1 when there's only 1 thread using the setValue()?

I'm not really into swing and their invokelater stuff, I've seen it before, but Swing doesn't interest me as much as .. ye well back-end.
What would be the worst case scenario without the swingutilities?

#4
farrell2k

farrell2k

    Learning Programmer

  • Members
  • PipPipPip
  • 60 posts

wim DC said:

Why should I care about number 1 when there's only 1 thread using the setValue()?

I'm not really into swing and their invokelater stuff, I've seen it before, but Swing doesn't interest me as much as .. ye well back-end.
What would be the worst case scenario without the swingutilities?

All UI updates need to happen on the event dispatch thread. Imagine you load your JFrame and it's in front of your face. You click a button and your progress bar starts updaging. Everything is fine. The setValue method of the progress bar is updating itself. Great. Now imagine that you alt-tab to open your web browser, then alt-tab back to your jframe. A repaint request is started on your jframe which calls repaints requests to every child component on the frame, including the progress bar. Now imagine what would happen if your frame tries to repaint your progress bar at the same time your setValue is painting its new value. Because both threads know nothing of one another, they both try to paint at the same time. What happens? Maybe a race condition that paints something wierd, or more likely a deadlock where each thread is waiting for the other to finish, which results in a lock-up. This could also happen when the event dispatch thread is repainting the button and your update thread is updating the progress bar. SwingUtilities.invokeLater() will take your setValue reqauest and execute it on the edt so that it does not interfere. This is why swing is not thread safe. Understand?

I re-read my initial post, and it could be interpreted as a criticism. It was not meant to criticize. I enjoyed your tutorial and look forward to more. Maybe you can talk about the keyword volatile.

#5
wim DC

wim DC

    Writes binary right handed and hex left handed

  • Members
  • PipPipPipPipPipPipPipPipPip
  • 2,084 posts
  • Programming Language:Java, JavaScript, PL/SQL
  • Learning:Java
Hey, thanks for the headsup, I will update this tutorial a bit when I got some time, in the weekend at its latest.
I've googled a bit and ye, the invokeLater seems to be the way to do repaints and stuff.

Now I digged a bit deeper and noticed that the java.awt.Component class (which pretty much all giu objects extend from in some way) does this in its repaint:
Toolkit.getEventQueue().postEvent(e);
Which would make me think it allready makes sure that the repaint events happen on the EDT... But ye, apparently it doesn't, why would I otherwise read so many blogs and posts about SwingUtilities.invokeLater() to repaint :P

I actually planned to handle .wait() and .notify() (of the Object class) for the next thread thingy.
For volatile I'll need to do some more research and experimenting. I literally never used it before :)
The little I know about is that each thread would get their own copy of the variable's value, but when the thread dies, I suppose the thread's copy should merge back with the original. How exactly that happens, what the rules are, and what about concurrency... no idea.

Edited by wim DC, 01 September 2011 - 06:21 AM.


#6
wim DC

wim DC

    Writes binary right handed and hex left handed

  • Members
  • PipPipPipPipPipPipPipPipPip
  • 2,084 posts
  • Programming Language:Java, JavaScript, PL/SQL
  • Learning:Java
Okay, I hope I did the SwingWorker correctly :)
you should find it halfway the tutorial, CTRL+F and look for SwingWorker.



#7
farrell2k

farrell2k

    Learning Programmer

  • Members
  • PipPipPip
  • 60 posts

wim DC said:

Okay, I hope I did the SwingWorker correctly :)
you should find it halfway the tutorial, CTRL+F and look for SwingWorker.


It looks great.

To comment on your post about repaint(), repaint, invalidate, revalidate are all thread safe methods you can call from any thread.




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users