Java第九课——多线程
首先先了解一下什么是多线程:
多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。
通俗的说就是在同一时间做多个任务,他们之间不能有先后顺序,而是同时进行。
那么为什么要使用多线程呢?
举个例子,比如飞机大战游戏,假如按一下空格键就会发射一颗子弹,如果想实现子弹连发,就需要按下多次空格键。如果不使用多线程,就需要等上一个子弹飞出去了,完成了它应该要走的轨迹,下一颗子弹才会出发。使用多线程的话,二者执行没有影响,按下空格就会往发射子弹,子弹的先后顺序只取决于按下空格键的顺序,与上一颗子弹是否到终点无关。
那么如何实现多线程?
线程的实现的实质是通过继承Thread类或是实现Runnable接口,重写里面的run方法来实现,具体如何实现见下面例子
我们以画小球的移动为例:鼠标按下,小球向右”跑“,并且没有"先后顺序"
首先需要一个界面和一个监听器,并完成小球的移动
public class Frame{
public void showUI(){
JFrame frame=new JFrame();
FlowLayout Layout=new FlowLayout();
Listener l=new Listener();
frame.setSize(600,600);
frame.setLocationRelativeTo(null);
frame.setLayout(Layout);
frame.addMouseListener(l);
frame.setVisible(true);
Graphics g=frame.getGraphics();
l.g=g;
}
public static void main(String[] args) {
Frame f=new Frame();
f.showUI();
}
public class Listener implements MouseListener{
private int x;
private int y;
Graphics g;
public void mousePressed(MouseEvent e) {
x=e.getX();
y=e.getY();
for(int i=0;i<200;i+=20){
g.setColor(Color.BLACK);
g.fillOval(x+i, y, 20, 20);
try{//休眠100ms,使小球的轨迹可见
Thread.sleep(100);
}catch (Exception a){
}
g.setColor(new Color(238,238,238));
g.fillOval(x+i, y, 20, 20);
}
//小球停下时的位置
g.setColor(Color.BLACK);
g.fillOval(x+200, y, 20, 20);
}
}
上面的代码实现了基本的移动,并没有加入线程,所以当鼠标点击多个地方时,小球是一个一个出来的,而不是点下立刻就出来的。那么接下来加入线程
在加入之前先介绍一下队列:
ArrayList,队列类似于一个数组,用来存放数据,和数组一样这个数据可以是int,float,string或是类的对象。但区别是数组是在初始化时就固定了长度的,但队列不需要限制长度,就如同排队一样,一个接一个往后站。
初始化的方法是:
int类型:ArrayList< int > list = new ArrayList < int > ();
类的对象:ArrayList< Ball > list=new ArrayList< Ball >();
为了实现小球之间实现不影响,我们引入队列ArrayList,每次按下鼠标就会把一个小球放进队列,这样小球之间就没有先后执行的关系了。再然后,另外写一个控制小球的类,并继承Thread,这个类中线程的作用是从队列里面拿出小球,并调用封装好的小球的方法实现球的移动,这个类要写run方法用于启动线程。到这里就实现了每个小球互不影响
综上另外写两个个类,一个定义为小球类,包括小球本身的运动方法和画小球的方法;另一个类为控制小球的类,这个类要继承Thread。
小球类:包括自己的移动方法
public class Ball {
private int x;
private int y;
Graphics g;
public Ball(int x,int y,Graphics g){
this.x=x;
this.y=y;
this.g=g;
}
public void move(){
x+=20;
}
public void draw(){
g.setColor(Color.BLACK);
g.fillOval(x, y, 20, 20);
try{
Thread.sleep(150);
}catch(Exception e){
}
g.setColor(new Color(238,238,238));
g.fillOval(x, y, 20, 20);
}
}
BallControl类:继承Thread并写run方法
public class BallControl extends Thread{
private ArrayList< Ball > list=new ArrayList< Ball >();
public BallControl(ArrayList<Ball> list){
this.list=list;
}
public void run(){
while(true){
for(int i=0;i<list.size();i++){
Ball b=list.get(i);
b.move();
b.draw();
}
try{
Thread.sleep(15);
}catch(Exception e){
}
}
}
}
然后在Listener里面添加小球
public class Listener implements MouseListener{
private int x;
private int y;
Graphics g;
private ArrayList<Ball> list=new ArrayList<Ball>();
public Listener(ArrayList<Ball> list){
this.list=list;
}
@Override
public void mousePressed(MouseEvent e) {
x=e.getX();
y=e.getY();
Ball b=new Ball(x,y,g);
list.add(b);
}
]
最后只要在界面里启动线程就好了
public void showUI(){
...//前面就省略了
BallControl bc=new BallControl(list);
bc.start();
}
最后实现效果可能并不好,没有加入缓存画布,具体内容在下下次课飞机大战的实现会讲解
另外,一种傻瓜式的线程方法Timer和TimerTask。
Timer是一个定时器,通过schedule(TimerTask task,long delay,long period)方法来使定时器工作,delay是指启动定时任务前的延迟,period是每过period时间就调用一次。
TimerTask是一个任务,需要在里面写run方法:
TimerTask task=new TimerTask(){
public void run(){
}
};//最后有有个分号
总的结构式:
Timer timer = new Timer();
TimerTask task=new TimerTask(){
public void run(){
}
};
timer.schedule(task,1000,1000);