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.
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.
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
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.
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().
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.


Sign In
Create Account



Back to top









