首先,严格按照操作系统理论来说,多线程并没有实现真正的同时进行。而是CPU将工作时间分成很多很短的时间片(Time slicing),每个时刻只能执行一个线程。
主要有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口。两种方式,除了创建线程实例的方式有差异外,启动线程都是调用start方法。
Runnable接口中只有一个void run()
方法,因此,实现Runnable接口需要实现run()方法。而启动多线程是通过将接口的实现类传入Thread进行实例化,然后调用start()方法。
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}
}
当然也可以通过匿名内部类实现临时的线程:
new Thread(new Runnable() {
public void run() {
System.out.println("Hello from a thread!");
}
}).start();
Hint:由于类的继承是单一的,只能有一个父类,因此在类已经继承一个父类时,可以通过实现Runnable接口来实现线程。
Thread类本身也是通过实现Runnable接口实现的:
public class Thread extends Object implements Runnable
同样需要重写自己的run方法,启动线程也是调用start方法:
public class HelloThread extends Thread {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
HelloThread p = new HelloThread();
p.start();
}
public static void main(String args[]) {
(new HelloThread()).start();
}
}
实现动画,就是让物体动起来,也就是说每次绘图时有规律的改变物体的位置即可。这里有两种实现方法,一种是将继承JPanel的类作为一个线程,一种是将物体类作为一个线程。两种方式实现效果差别不大,但前者更适用于多个物体一起改变位置的情况(同步),后者适用于物体各自改变位置的情况。
创建一个类,继承JPanel类,实现Runnable接口。在其中重写paint方法,然后实现run()方法,run方法中调用repaint()方法,并使用循环和休眠来实现动画效果。
package test;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* MyPanel.java.
* @author Kaiyan Zhang
*/
public class MyPanel extends JPanel implements Runnable{
int x = 0,y = 400;
@Override
public void paint(Graphics g){
super.paint(g);
g.drawOvel(x, y, 20, 20);
}
@Override
public void run(){
while(true){
if(x>800)
x = 0;
else
x = x + 10;
this.repaint();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String [] args){
MyPanel p = new MyPanel();
/* panel thread, paint the monkey */
Thread panelThread = new Thread(p);
JFrame frame = new JFrame();
frame.add(p);
frame.setSize(800, 800);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
/* begin to paint */
panelThread.start();
}
}
当然,需要绘制的物体也可以作为一个线程,每次改变位置,然后休眠,将物体传入MyPanel进行绘制。这里物体和MyPanel的休眠时间问题,后者的休眠时间更短一些,效果更好。
Hint: 重写paint方法时,一定要先调用父类的paint方法(super.paint(g)),这里后面才能使用repaint进行重写绘制。
首先实现一个JPanel的子类MyPanel,在paint()方法中实现物体的绘制;然后创建一个运动物体类,继承Thread类,将MyPanel作为参数传入,实现run()方法,当然可以有一个位置成员变量,run方法中每次改变位置后,调用MyPanel.repaint()方法实现新的位置绘图,每次绘制后休眠一段时间。
package test;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* MyPanel.java.
* @author Kaiyan Zhang
*/
public class MyPanel extends JPanel {
int x = 0, y = 400;
Ball b;
public MyPanel(Ball b) {
this.b = b;
b.start();
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.drawOvel(b.x, b.y, 20, 20);
}
public static void main(String[] args) {
Ball b = new Ball();
MyPanel p = new MyPanel(b);
b.setPanel(p);
/* panel thread, paint the monkey */
JFrame frame = new JFrame();
frame.add(p);
frame.setSize(800, 800);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
class Ball extends Thread {
int x = 0;
int y = 400;
MyPanel p;
public void setPanel(MyPanel p) {
this.p = p;
}
@Override
public void run() {
while (true) {
if (x > 800) {
x = 0;
} else {
x = x + 10;
}
p.repaint();
try {
sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
这里给出一个之前写的泡泡龙的代码,每次只有一个球进行运动,即使用该方法实现的
Bubble Shooter-使用线程实现泡泡龙