简单地说,线程就是程序中独立的运行片段,一个程序就是一个进程,而一个进程里面一般不会只有一个线程,多数情况是多条路线同时执行。
之前写的东西,画板也好,五子棋也好,都是单线程的,就是说一个程序里面从头到尾都只有一个线程,即执行顺序,只能执行完上一句才能继续下一句。
但是现实生活中显然不是这样的,一种事物或现象的出现肯定是多个因素同时影响的结果,既然面向对象是模拟现实,自然一个程序的运行结果也要有几个不同线程来共同实现。
在Java中实现多线程的两种方法:
1、创建一个类去继承Thread类,重写Thread类中的run方法。
2、创建一个类实现Runnable 接口,实现接口中的run方法。在创建新的线程对象的时候,通过构造方法传入这个类的对象。
不管是哪种方法,线程所执行的操作都是run方法里面的内容。
我们要实现多线程需要的操作一般有三步:
1、定义线程中的run方法里的内容,即线程的执行语句。
2、建立用户线程实例。
3、启动线程,调用线程对象的start方法。
以下是用线程实现的弹球游戏。
package 线程;
public interface Config {
final static int SIZE = 50;
final static int X = 20;
final static int Y = 20;
}
主线程:
package 线程;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class XianCheng {
private JPanel jp2;
private ArrayList<MyThread> list = new ArrayList();
public static void main(String[] args) {
XianCheng xc = new XianCheng();
xc.initUI();
}
public void initUI(){
//实例化窗体、面板、按钮...
JFrame jf = new JFrame("线程-------------------");
JPanel jp1 = new JPanel();
jp2 = new JPanel();
JButton jb1 = new JButton("Start");
JButton jb2 = new JButton("Pause");
JButton jb3 = new JButton("Resume");
JButton jb4 = new JButton("Stop");
//设置窗体大小
jf.setSize(700,700);
//设置关闭窗体时退出程序
jf.setDefaultCloseOperation(3);
//设置窗体居中显示
jf.setLocationRelativeTo(null);
//实例化监听器对象,直接传入窗体对象,而不是画布
MyListener l = new MyListener(jp2);
//获取到队列
list = l.getList();
//添加监听器
jb1.addActionListener(l);
jb2.addActionListener(l);
jb3.addActionListener(l);
jb4.addActionListener(l);
//把按钮添加到面板上
jp1.add(jb1);
jp1.add(jb2);
jp1.add(jb3);
jp1.add(jb4);
//把面板添加到窗体上
jf.add(jp1,BorderLayout.NORTH);
jf.add(jp2,BorderLayout.CENTER);
//设置窗体可见
jf.setVisible(true);
//获取面板上的画布对象
Graphics g = jp2.getGraphics();
//实例化图片对象
ImageIcon ima = new ImageIcon("Imas/bj.jpg");
//图片坐标
int x = 0;
int y = 0;
//图片移动速度
int yy = 0;
int vy = 0;
//图片移动加速度
int addvy = 1;
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//创建缓冲图片对象
BufferedImage buffer = new BufferedImage(jp2.getWidth(),jp2.getHeight(),
BufferedImage.TYPE_INT_RGB);
//获取到缓冲图片对象中的画布
Graphics g2 = buffer.getGraphics();
//将背景图画到缓冲图片上
g2.drawImage(ima.getImage(), 0, 0, null);
//取出队列中的ball对象,调用画球的方法
for(int i =0; i<list.size() ;i++){
MyThread ball = list.get(i);
ball.DrawBall(g2);
}
//这里画两张图的目的是在图片移动的时候两张图有衔接
g.drawImage(buffer, x, y, null);
g.drawImage(buffer, x+buffer.getWidth(), y, null);
x--;
if(x+buffer.getWidth()<0){
x=0;
}
}
}
}
监听器:
package 线程;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.*;
public class MyListener implements ActionListener{
private JPanel jp;
private ArrayList<MyThread> list = new ArrayList();
//构造方法,传入要画图的面板
public MyListener(JPanel jp){
this.jp = jp;
}
//获取队列的方法
public ArrayList getList(){
return list;
}
//点击按钮的处理方法
public void actionPerformed(ActionEvent e) {
//获取按钮的文本值
String s =e.getActionCommand();
//如果点击Start按钮
if(s.equals("Start")){
MyThread m1 = new MyThread(jp,list);
m1.start();
list.add(m1);
}
//如果点击Pause按钮
if(s.equals("Pause")){
//循环遍历队列取出队列中的对象,设置暂停标志位为true
for(int i = 0 ;i<list.size();i++){
MyThread m1 = list.get(i);
m1.setPauseFlag(true);
}
}
//如果点击Resume按钮
if(s.equals("Resume")){
//循环遍历队列取出队列中的对象,设置暂停标志位为false
for(int i = 0 ;i<list.size();i++){
MyThread m1 = list.get(i);
m1.setPauseFlag(false);
}
}
//如果点击Stop按钮
if(s.equals("Stop")){
//把队列中的对象全部清空
while(!list.isEmpty()){
list.remove(0);
}
}
}
画球的线程:
package 线程;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MyThread extends Thread {
private JPanel jp;
private int X,Y,R;//坐标、半径
private int change_x,change_y;//速度
private Color color;
private Graphics2D g;
//暂停和停止的标志位
private boolean pauseFlag = false,stopFlag = false;
//存放小球对象的队列
private ArrayList<MyThread> list;
//记录小球撞击之前的坐标
private int oldx,oldy;
public MyThread(JPanel jp,ArrayList<MyThread> list){
//属性初始化
this.jp = jp;
this.list = list;
Random r = new Random();
//给速度、半径和颜色随机赋值
change_x = r.nextInt(2)+1;
change_y = r.nextInt(2)+1;
R = r.nextInt(40)+20;
color = new Color(r.nextInt(256),r.nextInt(256),r.nextInt(256));
X = R;
Y = R;
}
public void run(){
//设置为一直循环
while(true){
//每次循环休眠5毫秒
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//判断是否停止,如果停止了就返回
if(stopFlag){
return;
}
//判断是否暂停,如果暂停就不执行下面的代码
if(pauseFlag){
continue;
}
//调用改变坐标的方法
changeXY();
}
}
//判断坐标,改变速度
public void changeXY(){
//记录原来的坐标,小球撞击时不会粘在一起
oldx = X;
oldy = Y;
//先改变坐标
X+=change_x;
Y+=change_y;
//调用check方法,判断小球之间是否撞击
check();
//如果小球运动到边界则改变方向
if(X+R>jp.getWidth()){
change_x = -change_x;
}
if(X-R<0){
change_x = Math.abs(change_x);
}
if(Y+R>jp.getHeight()){
change_y = -change_y;
}
if(Y-R<0){
change_y = Math.abs(change_y);
}
}
//暂停
public void setPauseFlag(boolean b){
pauseFlag = b;
}
//停止
public void setStopFlag(boolean b){
stopFlag = b;
}
//画球的方法
public void DrawBall(Graphics g){
int red = color.getRed();
int green = color.getGreen();
int bule = color.getBlue();
int xx = X;
int yy = Y;
int rr = R;
//利用循环画出立体效果的圆
for(int i = 0 ;i<50;i++){
red+=4;
green+=4;
bule+=4;
if(red>255)
red=255;
if(green>255)
green=255;
if(bule>255)
bule=255;
Color c= new Color(red,green,bule);
g.setColor(c);
g.fillOval(xx-rr,yy-rr, rr*2 ,rr*2);
xx+=1;
yy+=1;
rr-=2;
}
}
//判断小球之间是否碰撞
public boolean check(){
for(int i = 0;i<list.size();i++){
//利用循环取出队列中的对象
MyThread b = list.get(i);
//如果是它本身就不需要判断
if(b == this)
continue;
//利用勾股定理
int x0 = b.X - this.X;
int y0 = b.Y - this.Y;
int r0 = b.R + this.R;
//算出两球之间的距离
int distence = (int) Math.sqrt(x0*x0+y0*y0);
//如果距离小于半径之和就代表相撞
if(distence < r0){
//互相交换水平速度
int temp_x = this.change_x;
this.change_x = b.change_x;
b.change_x = temp_x;
//互相交换竖直速度
int temp_y = this.change_y;
this.change_y = b.change_y;
b.change_y = temp_y;
X = oldx;
Y = oldy;
return true;
}
}
return false;
}
}