一个多线程的例子

每一个任务称为一个线程,同时可以运行一个以上的程序称为多线程程序。

多线程与多进程本质的区别在于每个进程拥有自己的一套变量,而线程则共享数据。与进程相比,线程更轻量级,创建撤销一个线程比启动新进程容易的多。

以下程序中,当点击start后有一个小球开始在弹跳,但这个程序是一个单线程的,当球跳动时,点击close,程序不会结束,因为线程被球弹跳所占用,无法马上响应close。

Ball.java记录了运动的小球的坐标(x,y),并且有move方法将小球移动到下一个位置。

 

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.ArrayList;
import javax.swing.JPanel;

public class BallComponent extends JPanel {
	private ArrayList<Ball> balls=new ArrayList<Ball>();

	/*
	 * add a new ball to balls
	 */
	public void add(Ball b){
		balls.add(b);
	}
	
	/*
	 * paint the panel
	 */
	
	public void paintComponent(Graphics g){
		super.paintComponent(g);
		Graphics2D g2=(Graphics2D)g;
		g2.setPaint(Color.RED);
		for(Ball b:balls){
			g2.fill(b.getShape());
		}
	}	
}

 

 

BallComponent.java继承了JPanel,用来画图。

 

 

import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;

public class Ball {
	
	private static final int XSIZE=15;
	private static final int YSIZE=15;
	private double x=0;
	private double y=0;
	private double dx=1;
	private double dy=1;
	
	/*
	 * change the value of x and y of the next position of ball 
	 */
	public void move(Rectangle2D bounds){
		x+=dx;
		y+=dy;
		if(x<bounds.getMinX()){
			x=bounds.getMinX();
			dx=-dx;
		}
		if(x+XSIZE>=bounds.getMaxX()){
			x=bounds.getMaxX()-XSIZE;
			dx=-dx;
		}
		if(y<bounds.getMinY()){
			y=bounds.getMinY();
			dy=-dy;
		}
		if(y+YSIZE>=bounds.getMaxY()){
			y=bounds.getMaxY()-YSIZE;
			dy=-dy;
		}
	}
	
	/*
	 * the shape of the ball at current position
	 */
	public Ellipse2D getShape(){
		return new Ellipse2D.Double(x,y,XSIZE,YSIZE);
	}

}

 

 BounceFrame.java完成整个界面,点击start按钮时调用addBall方法。

 

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;


public class BounceFrame extends JFrame {
	private static final int DEFAULT_WIDTH=450;
	private static final int DEFAULT_HEIGHT=350;
	private static final int STEPS=10000;
	private static final int DELAY=3;
	private BallComponent comp;
	
	public BounceFrame(){
	    setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);
	    comp = new BallComponent();
	    add(comp,BorderLayout.CENTER );
	    
	    JPanel buttonPanel=new JPanel();
	    addButton(buttonPanel,"Start",new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				addBall();
			}

	    });
	    addButton(buttonPanel,"Close",new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
	    });
	    add(buttonPanel,BorderLayout.SOUTH );
	}

	/*
	 * add button and listener to Container
	 */
	private void addButton(JPanel c, String title,
			ActionListener listener) {
		JButton button=new JButton(title);
		c.add(button);
		button.addActionListener(listener);
	}
	

	private void addBall() {
		try{
			Ball ball=new Ball();
			comp.add(ball);
			for(int i=0;i<STEPS;i++){
				ball.move(comp.getBounds());
				comp.paint(comp.getGraphics());//如果调用repaint,不会重画面板,因为addBall完全掌握这控制权
				Thread.sleep(DELAY);
			}
		}catch(InterruptedException e){
			
		}
	}
 
}
 

然后是程序的入口:

 

import java.awt.EventQueue;
import javax.swing.JFrame;

public class Bounce {
	public static void main(String[] arg){
		EventQueue.invokeLater(new Runnable(){
			public void run(){
				JFrame frame=new BounceFrame();
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				frame.setVisible(true);
			}
		});
	}
}

 

 要把这个程序改为多线程,要做如下改变:

如果将球移动的代码放在一个独立的线程中,这样每个球都在自己的线程中运行。由于每个线程都有机会得到运行,所以在在球弹跳期间用户点击close时,事件调度线程有机会关注到这一动作。

要想把弹跳球代码放在一个独立的线程中,只要实现Runnable接口,将动画代码放在run中。代码如下:

 

BallRunnable implements Runnable{
   ... ...
public void run() {
		try{
			for(int i=0;i<STEPS;i++){
				ball.move(comp.getBounds());
				comp.repaint();
				Thread.sleep(DELAY);
			}
		}catch(InterruptedException e){
			
		}
	}
   ... ...
}

 

 当点击start后启动一个新线程:

 

Ball ball=new Ball();
comp.add(ball);
Runnable r=new BallRunnable(ball,comp);
Thread t=new Thread(r);
t.start();
 

也可以继承Thread类来定义一个线程,然后再点击start后构造一个子类对象,启动线程。这种方法已经不再推荐,要为每一个任务创建独立的线程付出的代价太大。

 

import java.awt.Component;

public class BallRunnable implements Runnable{
	private Ball ball;
	private Component comp;
	private static final int STEPS=100000;
	private static final int DELAY=3;
	
	public BallRunnable(Ball ball,Component comp){
		this.ball=ball;
		this.comp=comp;
	}

	public void run() {
		try{
			for(int i=0;i<STEPS;i++){
				ball.move(comp.getBounds());
				comp.repaint();
				Thread.sleep(DELAY);
			}
		}catch(InterruptedException e){
			
		}
	}
}

 

 改进后的BounceFrame.java

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class BounceFrame extends JFrame{
	private static final int DEFAULT_WIDTH=450;
	private static final int DEFAULT_HEIGHT=350;
	private BallComponent comp;
	
	public BounceFrame(){
	    setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);
	    comp = new BallComponent();
	    add(comp,BorderLayout.CENTER );
	    
	    JPanel buttonPanel=new JPanel();
	    addButton(buttonPanel,"Start",new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				addBall();
			}

	    });
	    addButton(buttonPanel,"Close",new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
	    });
	    add(buttonPanel,BorderLayout.SOUTH );
	}

	/*
	 * add button and listener to Container
	 */
	private void addButton(JPanel c, String title,
			ActionListener listener) {
		JButton button=new JButton(title);
		c.add(button);
		button.addActionListener(listener);
	}
	

	private void addBall() {
			Ball ball=new Ball();
			comp.add(ball);
			Runnable r=new BallRunnable(ball,comp);
			Thread t=new Thread(r);
			t.start();
		}
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值