I am totally a newbie in Java. And I want to create an animation. But I did not succeed. mouseClicked() will be executed in the event dispatching thread. Why doesn't it work? Will the event dispatching be occupied by other threads, what other threads?
public class DemoThreadGUI {
public static void main(String [] args) {
DemoThreadGUI app = new DemoThreadGUI();
app.go();
}
public void go() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
generateGUI();
}
});
}
public void generateGUI() {
JFrame frame = new JFrame("Demo");
frame.add(new MyPanel());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
class MyPanel extends JPanel implements MouseListener {
private int x,y;
private int r;
public MyPanel() {
this.setPreferredSize(new Dimension(100,100));
this.addMouseListener(this);
x = 50;
y = 50;
r = 25;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x-r,y-r,r*2,r*2);
}
public void mouseClicked(MouseEvent event) {
int targetX = event.getX();
int targetY = event.getY();
for(int i=0;i<10;++i) {
x = (x+targetX)/2;
y = (y+targetY)/2;
repaint();
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
}
}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public void mousePressed(MouseEvent event) {}
public void mouseReleased(MouseEvent event) {}
}
}
In your mouseClicked() start a new Thread and place the code in the thread
for(int i=0;i<10;++i) {
x = (x+targetX)/2;
y = (y+targetY)/2;
repaint();
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
}
The repaint() call should be wrapped into SwingUtilities.invokeAndWait() to pass control to EDT
As Holger said, You can’t expect a thread to paint your UI when you told it to sleep.
Use this instead:
Make targetX and targetY global variables, and make a new java.util.Thread object t.
private int x,y;
private int r;
Thread t;
int targetX, targetY;
modify your mouseClicked method as:
public void mouseClicked(final MouseEvent event) {
targetX = event.getX();
targetY = event.getY();
t = new Thread(new Runnable(){public void run(){anim();}});
t.start();
}
Put your code of the mouseClicked method into the anim method as:
public void anim()
{
for(int i=0;i<10;++i) {
try{
x = (x+targetX)/2;
y = (y+targetY)/2;
repaint();
Thread.sleep(100);
}catch(Exception e){}
}
t.stop();
}
This works perfectly.
Related
I'm experimenting on a GUI that I programmed and I don't understand how I can fix my problem:
My GUI contains a jPanel that on receiving a mouseclick, paints a point with filloval command.
private void myPnlMousePressed(java.awt.event.MouseEvent evt) {
changed = true;
p.x = evt.getX();
p.y = evt.getY();
drewPoints(p.x, p.y);
}
private void drewPoints (int x, int y) {
if (gf == null) {
gf = (Graphics)myPnl.getGraphics();
}
myPointsList.add(new Point(x, y));
gf.fillOval(x, y, 5, 5);
xVal.setText("X = " + x);
yVal.setText("Y = " + y);
}
everything works fine but when I want to open an XML file that I created to save all the points it doesn't work.
The problem is that when I use the repaint method on the jPanel after choosing a file, all the points loads fine but the panel can't draw the points.
If I put the repaint method in the open button listener (before the choosing file) it works, but then if the user cancels the open option so the panel stays blank and I don't want to draw the points again.
I think it happens because the repaint process is not finished.
All the points added to a private List.
private void OpenFile() {
try {
File thisFile;
JFileChooser of = new JFileChooser();
int option = of.showOpenDialog(of);
if (option == JFileChooser.APPROVE_OPTION){
thisFileName = of.getSelectedFile().getPath();
thisFile = new File(thisFileName);
if (!of.getSelectedFile().getName().endsWith(".xml")) {
String error = "Error, You didn't select XML file";
JOptionPane.showMessageDialog(this, error, "Wrong type of file", JOptionPane.INFORMATION_MESSAGE);
return;
}
myPnl.repaint();
myPointsList.clear();
....
....
....
for (int i = 0; i < pointsList.getLength(); i++) {
Element point = (Element) pointsList.item(i);
p.x = Integer.parseInt(point.getElementsByTagName("X").item(0).getTextContent());
p.y = Integer.parseInt(point.getElementsByTagName("Y").item(0).getTextContent());
drewPoints(p.x, p.y);
}
....
how can I make it work??
Don't use gf = (Graphics)myPnl.getGraphics();, this is not how painting in Swing works. The getGraphics method can return null and is nothing more then a snap shot of the last paint cycle, any thing you paint to it will be erased on the next paint cycle (repaint).
Instead, override the JPanels paintComponent and put all you painting logic there. There is an expectation that when called, you are expected to fully re-paint the current state of the component.
See Painting in AWT and Swing and Performing Custom Painting for more details about how painting works in Swing
You have to use the repaint() and override the paint() method:
class MyPanel extends JPanel implements MouseListener
{
private int x;
private int y;
public MyPanel() {
super();
addMouseListener(this);
}
#Override public void mouseEntered(MouseEvent e) { }
#Override public void mouseExited(MouseEvent e) { }
#Override public void mouseClicked(MouseEvent e) { }
#Override public void mousePressed(MouseEvent e) { }
#Override public void mouseReleased(MouseEvent e) {
x = e.getX();
y = e.getY();
repaint();
}
#Override public void paint(Graphics g) {
super.paint(g);
g.fillOval(x, y, 10, 10);
}
}
If you want to draw all points, don't use x and y but a list of points:
class MyPanel extends JPanel implements MouseListener
{
private ArrayList<Point> points = new ArrayList<>();
public MyPanel() {
super();
addMouseListener(this);
}
#Override public void mouseEntered(MouseEvent e) { }
#Override public void mouseExited(MouseEvent e) { }
#Override public void mouseClicked(MouseEvent e) { }
#Override public void mousePressed(MouseEvent e) { }
#Override public void mouseReleased(MouseEvent e) {
points.add(new Point(e.getX(), e.getY()));
repaint();
}
#Override public void paint(Graphics g) {
super.paint(g);
for (Point p : points)
g.fillOval(p.getX(), p.getY(), 10, 10);
}
}
where:
class Point
{
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
Then use it:
public static void main(String[] args) {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
MyPanel myPanel = new MyPanel();
frame.add(myPanel);
frame.setVisible(true);
}
I am currently using a JPanel to draw the elements from my game, however, there is occasional stuttering with the graphics, after some research I learnt about screen tearing and double buffering, which I believe is the solution to my problem, however, currently with my code I am finding it extremely difficult to implement without tearing out a lot of code.
I am wondering if anyone could possibly provide either a simpler way to fix my screen tearing on a small 2D tile based game, or how to do double buffering with my current code, thank you very much!
Frame.java
public class Frame extends JPanel implements KeyListener {
public static Game game;
public static boolean[] mouseDown = new boolean[4];
public static int width, height;
public static Font font = new Font("Arial", Font.BOLD, 16);
public static JFrame frame;
public Frame()
{
setFocusable(true);
requestFocus();
setOpaque(true);
addKeyListener((KeyListener) this);
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
});
addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseMoved(MouseEvent e) {
}
#Override
public void mouseDragged(MouseEvent e) {
}
});
}
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setBackground(Color.BLACK);
g2d.setFont(font);
super.setBackground(Color.BLACK);
super.paintComponent(g2d);
//This is where I use g2d to draw onto the JPanel
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
frame = new JFrame("Game");
frame.add(new Frame());
frame.setSize(1000, 750);
frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}});
}
Game.java
public void gameStart() {
Thread gameThread = new Thread() {
// Override run() to provide the running behavior of this thread.
#Override
public void run() {
gameLoop();
}
};
// Start the thread. start() calls run(), which in turn calls gameLoop().
gameThread.start();
}
public void gameLoop() {
while (!isGameFinished) {
beginTime = System.nanoTime();
gameUpdate();
frame.repaint();
// Delay timer to provide the necessary delay to meet the target rate
timeTaken = System.nanoTime() - beginTime;
timeLeft = (UPDATE_PERIOD_NSEC - timeTaken) / 1000000L; // in milliseconds
if (timeLeft < 10) timeLeft = 10; // set a minimum
try { // Provides the necessary delay and also yields control so that other thread can do work.
Thread.sleep(timeLeft);
} catch (InterruptedException ex) { }
}
}
public void gameStart() {
Thread gameThread = new Thread() {
// Override run() to provide the running behavior of this thread.
#Override
public void run() {
gameLoop();
}
};
// Start the thread. start() calls run(), which in turn calls gameLoop().
gameThread.start();
}
public void gameLoop() {
while (!isGameFinished) {
beginTime = System.nanoTime();
gameUpdate();
frame.repaint();
timeTaken = System.nanoTime() - beginTime;
timeLeft = (UPDATE_PERIOD_NSEC - timeTaken) / 1000000L;
if (timeLeft < 10) timeLeft = 10; // set a minimum
try {
Thread.sleep(timeLeft);
} catch (InterruptedException ex) { }
}
}
As shown in the code, the Game class contains the Game loop, which will constantly repaint the Frame class. How can I tweak this code to allow for double buffering to remove screen tearing? Thanks!
Double buffering is the best way to fix it. Try calling Toolkit#sync (Toolkit.getDefaultToolkit().sync()) , which sometimes removes the issue. You won't be tearing down much code if you switch to double buffering, just move the code in paint to a separate method.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I'm trying to learn Java and game programming by myself by just playing around with some different things. But now I have come across this problem, when my java app goes fullscreen via GraphicsDevice, the KeyListeners don't work. It's like it doesn't register anything when I press the buttons on my keyboard. When the app isn't fullscreen, everything works as it is supposed to.
I am using Mac.
The code is a bit messy, but it should be somewhat easy to navigate etc.
Game.class
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
// Render Vars
public static final int WIDTH = 1280;
public static final int HEIGHT = 800; //WIDTH / 16 *
public static final int SCALE = 1;
public final static String TITLE = "Test Game - inDev 1.0.0";
public static boolean fullscreen = false;
public static JFrame window = new JFrame(TITLE);
public static Font defaultFont = new Font("Dialog", Font.PLAIN, 12);
public static Color defaultColor = Color.gray;
// Thread Vars
public static boolean running = false;
private static Thread thread;
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
// Mechanic Vars
public static boolean mouseDown;
public static int mouseX;
public static int mouseY;
// Game Vars
public static GameState gameState = new Play();
public static boolean keyPressed;
public static Game game = new Game();
public static void main(String arghs[]) {
game.setSize(new Dimension(WIDTH, HEIGHT));
game.setPreferredSize(new Dimension(WIDTH, HEIGHT));
fullscreen = true;
window.add(game);
window.setUndecorated(true);
window.pack();
window.setLocationRelativeTo(null);
window.setResizable(false);
window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
window.setVisible(true);
// HERE I GO FULLSCREEN
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(window);
game.addKeyListener(new KeyEventListener());
game.addMouseListener(new MouseEventListener());
game.start();
}
public void run() {
init();
long lastTime = System.nanoTime();
final double numTicks = 100.0;
double ns = 1000000000 / numTicks;
double delta = 0;
int updates = 0;
int frames = 0;
long timer = System.currentTimeMillis();
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
if (delta >= 1) {
update();
updates++;
delta--;
}
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println(updates + " Ticks, FPS: " + frames);
updates = 0;
frames = 0;
}
}
stop();
}
private synchronized void start() {
if (running) {
return;
}
running = true;
thread = new Thread(this);
thread.setName("My Game");
thread.start();
}
public synchronized static void stop() {
if (!running) {
return;
}
running = false;
thread = null;
System.exit(1);
}
private void init() {
gameState.init();
}
private void update() {
gameState.update();
}
private void render() {
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setColor(defaultColor);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
// Draw Content
gameState.render(g);
// Draw to Screen;
g.dispose();
bs.show();
}
public static void setGameState(GameState state) {
gameState = state;
gameState.init();
}
}
KeyEventListener.class
public class KeyEventListener extends KeyAdapter {
public void keyPressed(KeyEvent e) {
if (!Game.keyPressed) {
Game.gameState.keyPressed(e);
}
Game.keyPressed = true;
}
public void keyReleased(KeyEvent e) {
Game.gameState.keyReleased(e);
Game.keyPressed = false;
}
}
MouseEventListener.class (Just for recording the mouse position)
public class MouseEventListener extends MouseAdapter {
public void mousePressed(MouseEvent e) {
Game.mouseDown = true;
}
public void mouseReleased(MouseEvent e) {
Game.mouseDown = false;
}
public void mouseMoved(MouseEvent e) {
Game.mouseX = e.getX();
Game.mouseY = e.getY();
}
public void mouseDragged(MouseEvent e) {
Game.mouseX = e.getX();
Game.mouseY = e.getY();
}
}
GameState.class
public abstract class GameState {
public abstract void init();
public abstract void update();
public abstract void render(Graphics g);
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
And my Play.class which is the current gameState
public class Play extends GameState {
public static int key;
public void init() {
}
public void update() {
}
public void render(Graphics g) {
g.drawString("Key: " + key, 100, 100);
}
public void keyPressed(KeyEvent e) {
key = e.getKeyCode();
}
public void keyReleased(KeyEvent e) {
}
}
KeyListener will only respond to key events when the component is registered to is focusable and has focus.
After you have set the window to full screen, try adding...
game.requestFocusInWindow();
You may also need to use game.setFocusable(true)
If it wasn't for the fact that you're using a Canvas, I'd suggest using the key bindings API to over all these focus issues
Updated
I added...
window.addWindowFocusListener(new WindowAdapter() {
#Override
public void windowGainedFocus(WindowEvent e) {
System.out.println("gainedFocus");
if (!game.requestFocusInWindow()) {
System.out.println("Could not request focus");
}
}
});
To the window after it was visible but before it was made full screen
Updated
Oh, you're going to love this...
Based on this question: FullScreen Swing Components Fail to Receive Keyboard Input on Java 7 on Mac OS X Mountain Lion
I added setVisible(false) followed by setVisible(true) after setting the window to full screen mode and it seems to have fixed it...
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(window);
window.setVisible(false);
window.setVisible(true);
Verified running on Mac, using Java 7
After execution, the loop completes its circles and then the repaint method is being called,
Is there an alternative way of drawing the image?
public class GameCanvas extends JPanel implements KeyListener {
private Tank tank1;
private Tank tank2;
private Rocket rocket;
public Graphics2D g2;
private Image img;
public boolean fire;
public GameCanvas() {
tank1 = new Tank(0, 0);
tank2 = new Tank(500, 500);
rocket = new Rocket(50, tank1);
init();
fire = false;
}
public void init() {
JFrame frame = new JFrame("Topak Tank");
frame.setLayout(new FlowLayout());
frame.setSize(1300, 740);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
this.setPreferredSize(new Dimension(1300, 740));
frame.add(this);
frame.addKeyListener(tank1);
frame.addKeyListener(this);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
tank1.draw(g);
// rocket.draw(g);
g2 = (Graphics2D) g;
if (fire == true) {
try {
img = ImageIO.read(getClass().getResource("rocket.png"));
System.out.println(rocket.getYPos());
g2.drawImage(img, rocket.getXPos(), rocket.getYPos(), null);
} catch (Exception e) {
}
}
repaint();
}
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_F: {
try {
int xPos = tank1.getXAxis();
int yPos = tank1.getYAxis();
fire = true;
int delay = 10000;
// img = ImageIO.read(getClass().getResource("rocket.png"));
int count = 0;
while (true) {
System.out.println(xPos);
System.out.println(yPos);
if (count == 5) {
break;
}
count++;
// g2.drawImage(img,xPos,yPos,null);
rocket.setYPos(yPos);
System.out.println("loop");
Timer t = new Timer(delay, null);
t.start();
yPos++;
this.repaint();// here i want to excute and draw image after every lop cycle
Thread.sleep(100);
}
System.out.println("Fired");
fire = false;
} catch (Exception io) {
JOptionPane.showMessageDialog(null, "Could not found Rocket image");
}
}
break;
}
}
public void keyReleased(KeyEvent ke) {
}
public void keyTyped(KeyEvent ke) {
}
public static void main(String[] arg) {
GameCanvas gc = new GameCanvas();
}
}
Calling repaint() just tells the component to repaint itself whenever the EDT is available again. Since that entire loop is on the EDT, the component can't repaint itself until it's done.
You should probably be using a Timer or a Thread instead of tying up the EDT with that loop. You should NOT be calling sleep() on the EDT.
More info here: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
I am trying to use one thread to control bouncing balls. I can add balls, delete balls, suspend the movement but when i try to resume the movement of bouncing balls, notify()/notifyAll() is not working. I want only one thread to control the movement of balls that are being added in a List. I would appreciate a simple explanation as I am a complete novice. Here is the code:
************************************************
public class BounceBallApp extends JApplet {
public BounceBallApp() {
add(new BallControl());
}
public static void main(String[] args) {
BounceBallApp applet = new BounceBallApp();
JFrame frame = new JFrame();
frame.add(applet); //new line added as per the reference
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Assig_1_Base");
frame.add(applet, BorderLayout.CENTER);
frame.setSize(400, 320);
frame.setVisible(true);
}
}
***************************************************************
public class BallControl extends JPanel {
private BallPanel ballPanel = new BallPanel();
private JButton jbtSuspend = new JButton("Suspend");
private JButton jbtResume = new JButton("Resume");
private JScrollBar jsbDelay = new JScrollBar();
private JButton jbtAddBall = new JButton("+1");
private JButton jbtDeleteBall = new JButton("-1");
public BallControl() {
JPanel panel = new JPanel();
panel.add(jbtSuspend);
panel.add(jbtResume);
panel.add(jbtAddBall);
panel.add(jbtDeleteBall);
ballPanel.add();
ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
jsbDelay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(jsbDelay.getMaximum());
setLayout(new BorderLayout());
add(jsbDelay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
// Register listeners
jbtSuspend.addActionListener(new ActionListener() {
#Override
public synchronized void actionPerformed(ActionEvent e) {
ballPanel.suspend();
}
});
jbtResume.addActionListener(new ActionListener() {
#Override
public synchronized void actionPerformed(ActionEvent e) {
ballPanel.resume();
}
});
jbtAddBall.addActionListener(new ActionListener() {
#Override
public synchronized void actionPerformed(ActionEvent e) {
ballPanel.add();
}
});
jbtDeleteBall.addActionListener(new ActionListener() {
#Override
public synchronized void actionPerformed(ActionEvent e) {
ballPanel.delete();
}
});
jsbDelay.addAdjustmentListener(new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(jsbDelay.getMaximum() - e.getValue());
}
});
}
}
public class BallPanel extends JPanel {
private int delay = 10;
private List<Ball> ballsArray = Collections.synchronizedList(new ArrayList());
//protected List<Ball> ballsArray = new ArrayList<>();
private int radius = 5;
boolean threadSuspended = false;
public BallPanel() {
start();
}
protected void start(){
Thread t;
t = new Thread(){
#Override
public void run(){
System.out.println("*************");
while (true){
repaint();
try {
Thread.sleep(delay);
synchronized(this){
if (threadSuspended==true) {wait();}
}
} catch (InterruptedException e){
e.getMessage();
}
}
}
};
t.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Ball ball : ballsArray){
g.setColor(ball.color);
if (ball.x < 0 || ball.x > getWidth())
ball.dx *= -1;
if (ball.y < 0 || ball.y > getHeight())
ball.dy *= -1;
ball.x += ball.dx;
ball.y += ball.dy;
g.fillOval(ball.x - radius, ball.y - radius, radius * 2, radius * 2);
}
}
public synchronized void suspend() {
threadSuspended = true;
}
public synchronized void resume() {
threadSuspended = false;
notify();
}
public void setDelay(int delay) {
this.delay = delay;
}
public void add(){
if (threadSuspended==false) ballsArray.add(new Ball());
}
public void delete(){
if (ballsArray.size() > 0 && threadSuspended==false)
ballsArray.remove(ballsArray.size() - 1); // Remove the last ball
}
}
**************************************************
public class Ball {
int x = 0;
int y = 0;
int dx = 2;
int dy = 2;
Color color = new Color(random(255),random(255),random(255));
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
}
You have a "context" issue...
You declare the Thread within the context of the BallPane...
So, when you call wait(), you are actually calling wait() on t
Thread t;
t = new Thread(){
#Override
public void run(){
/*...*/
// this = t
synchronized(this){
// This is the same as saying
// this.wait() or t.wait();
if (threadSuspended==true) {wait();}
}
}
};
But when you call notify, you are calling BallPane's notify method...
public synchronized void resume() {
threadSuspended = false;
// This is the same as this.notify or BallPane.this.notify()
notify();
}
So, t is waiting on t's monitor lock and you call BallPane's notify on it's monitor lock...meaning t will never be notified.
It would be better if you used a shared lock instead...
public class BallPanel extends JPanel {
protected static final Object SUSPEND_LOCK = new Object();
/*...*/
Thread t;
t = new Thread(){
#Override
public void run(){
/*...*/
synchronized(SUSPEND_LOCK ){
if (threadSuspended==true) {SUSPEND_LOCK.wait();}
}
}
};
/*...*/
public void resume() {
synchronized(SUSPEND_LOCK){
threadSuspended = false;
SUSPEND_LOCK.notify();
}
}