`
狼迹天涯
  • 浏览: 6862 次
  • 性别: Icon_minigender_2
文章分类
社区版块
存档分类
最新评论

线程的初步应用

    博客分类:
  • java
阅读更多
//线程初步应用——————————————————————————————————

 

   最近一段时间,我学习了有关线程的知识,今天来总结一下最近学到的知识。

我们的任务是要在窗体上绘制出一些能够互相碰撞的小球。这个功能的实现,需要用到线程相关知识。线程——进程中的某一个独立的运行单位。而多线程,是指几个线程同时进行,你在操作一个线程的时候,计算机自己运行其他的线程。(把多的任务交给计算机来做)。

在做的过程中分为了几部分

1、窗体的绘制

2、小球的绘制

3、按钮功能的实现

4、小球碰撞的实现

下面分别来介绍。

1、窗体的绘制,已经进行过多次了。就不再赘述。

2、小球的绘制

要将小球绘制在run方法中,因为我们要让小球自己去不停的运动,而我们要进行其他的操作。如果不将小球放于一个独立线程中,如果想要让小球一直运动,小球不停止,我们没有办法进行别的操作。

画小球不难,但是如何让其运动起来?在这里我们可以通过坐标的不断改变来移动小球。因此我们不断对一个已经绘制出来的小球进行擦除、移动、重绘的操作,就可以在窗体上形成不断移动的小球。让小球碰到窗体的边界反弹,则只需要加一个判断,当坐标到达边界时,使其改变正负即可。

3、按钮功能的实现

只需要在窗体上加几个按钮——“添加”、“暂停”、“恢复”,为其加上监听器即可。

“添加”的实现:重新启动一个小球的线程。

“暂停”的实现:遍历所有的小球,调用每一个小球的暂停方法。

“恢复”的实现:同暂停一样,遍历所有小球,调用每一个小球的恢复方法。

但是,在线程中,线程本身的暂停和恢复方法存在缺陷,即它无法确定在线程运行中的哪一步暂停。比如:现在这个小球的线程中,包含了小球的擦除、移动和重绘过程。我在点击“暂停”按钮的时候,无法确定它在哪一个过程暂停。如果在小球擦除后、重绘前暂停了,则会出现界面上小球消失的情况。

故,我们创建自己的暂停、恢复方法。(见代码)

4、小球碰撞的实现

要实现小球与小球之间的碰撞,我们需要在移动(改变坐标)的时候遍历每一个小球,判断其与当前小球的距离。当距离到达半径之和时,则需要改变方向。因此,需要用一个队列来保存每一个小球。在开启线程的时候,将小球保存在队列中。

 

代码如下:

 

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.JFrame;

//继承线程类,重写run方法
public class Ball extends Thread{

	
	//定义必要的私有属性
	private JFrame jf;
	private Graphics g;
	private ArrayList<Ball>balls;
	//球的半径
	private int radius;
	//球的位置坐标
	private int x;
	private int y;
	//球的颜色
	private Color color;
	//球运动的方向
	private int directX=1;
	private int directY=1;
	private int speedX=1;
	private int speedY=1;
	
	//因为系统自带的线程运行和暂停的方法有缺陷,故定义自己的方法
	//线程运行状态标记,赋初始值
	private boolean pauseFlag=false;
	//线程停止标记,赋初始值
	private boolean stopFlag=false;
	
	
	//在构造函数中传入界面,得到画布
	public Ball(JFrame jf,ArrayList<Ball> balls) {
		this.jf=jf;
		this.balls=balls;
		g=jf.getGraphics();
		
		//随机初始化参数
		Random random=new Random();
		
		//球的大小,通过改变半径的大小
		radius=random.nextInt(20)+15;
		//球的运动速度,通过改变运动增量来改变
		speedX=random.nextInt(5)+1;
		speedY=random.nextInt(5)+1;
		//球的颜色,通过改变三原色(0——255)
		color=new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255));
		
	}
	
	//重写run方法
	public void run(){
		
		while(true){
			try {
				sleep(40);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			//判断,如果没有暂停,则继续
			if(pauseFlag){
				continue;
			}
			
			//小球运动起来的步骤为:清除、运动、画,为了使代码更直观,设置三个外方法,再分别调用
			clear();
			
			//如果停止线程运行则结束run方法
			if(stopFlag){
				break;
			}
			
			move();
			
			draw();
			
		}
		
	}

	public void clear() {
		//在小球运动前用背景颜色把以前的球覆盖掉
//		g.setColor(jf.getBackground());
//		g.fillOval(x, y, radius, radius);
		
		fillCircle(x,y,radius+2,jf.getBackground());
	}
	
	public void move() {
		
		//接收原位置坐标
		int oldX=x;
		int oldY=y;
		
		if(directX==1){
			x+=speedX;
		}else{
			x-=speedX;
		}
		
		if(directY==1){
			y+=speedY;
		}else{
			y-=speedY;
		}
		
		//碰到边界改变方向
		if(x<radius){
			directX=Math.abs(directX);
		}
		if(x>jf.getWidth()-radius){
			directX=-Math.abs(directX);
		}
		if(y<radius){
			directY=Math.abs(directY);
		}
		if(y>jf.getHeight()-radius){
			directY=-Math.abs(directY);
		}
		
		//遍历所有小球,判断碰撞
		for(int i=0;i<balls.size();i++){
			
			Ball ball=balls.get(i);
			//判断是否是自己
			if(ball==this){
				continue;
			}
			
			//进行距离的判断
			//两个球在X方向的距离
			int xx=Math.abs(x-ball.x);
			//两个球在Y方向的距离
			int yy=Math.abs(y-ball.y);
			//两个球圆心的距离
			int len=(int) Math.sqrt(xx*xx+yy*yy);
			
			//判断是否撞上
			//当圆心距离小于两球半径之和时,撞上
			if(len<=radius+ball.radius){
				
				//判断是否改变方向和速度
				if(directX!=ball.directX){
					//改变方向
					directX=-directX;
					ball.directX=-ball.directX;
					
					//交换速度
					int tempspeed=speedX;
					speedX=ball.speedX;
					ball.speedX=tempspeed;
				}
				
				if(directY!=ball.directY){
					//改变方向
					directY=-directY;
					ball.directY=-ball.directY;
					
					//交换速度
					int tempspeed=speedY;
					speedY=ball.speedY;
					ball.speedY=tempspeed;
				}
				
				if(directX==ball.directX && directY==ball.directY){
					
					//如果为追击问题。则交换速度,方向不变
					
					int tempspeedX=speedX;
					speedX=ball.speedX;
					ball.speedX=tempspeedX;
					
					int tempspeedY=speedY;
					speedY=ball.speedY;
					ball.speedY=tempspeedY;
					
				}
				//解除粘连效果?????????
				x=oldX;
				y=oldY;
				
				
				
			}
		}
		
	}
	
	//要做出一个立体球的效果,需要多次画球,进行颜色以及大小的改变
	private Color getColor(int inc){
		
		int red=color.getRed();
		int green=color.getGreen();
		int blue=color.getBlue();
		
		red+=inc;
		green+=inc;
		blue+=inc;
		
		//判断颜色是否超出一定范围
		if(red>255){
			red=255;
		}
		if(green>255){
			green=255;
		}
		if(blue>255){
			blue=255;
		}
		
		return new Color(red, green, blue);
	}
	
	//因为使用原来的覆盖方法是画椭圆,清楚不干净,会留下一条“尾巴”,故设置自己的清楚方法
	private void fillCircle(int x,int y,int r,Color c){
		Graphics2D gg = (Graphics2D)g;
		//通过该方法的设置,使图形去除锯齿状,可以得到多么细腻的图形——会出现频闪的情况
		gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
		gg.setColor(c);
		gg.fillOval(x-r, y-r, 2*r, 2*r);//画圆的方法?????
		
	}
	
	
	
	public void draw() {
		//在新的位置画小球
//		g.setColor(color);
//		g.fillOval(x, y, radius, radius);
		
		//为了不破坏原变量,这里定义一个临时变量
		int r=radius;
		
		//也可以让光点偏移一些,更漂亮
		int xx=x;
		
		for(int i=0;r>13 && xx>=radius/2;i++){
			Color c=getColor(i*8);
			
			
			fillCircle(xx-1,y,r,c);
			r--;
			xx=xx-1;

		
		}
		
	}
	
	
	//线程暂停和恢复的方法
	public void pause(){
		pauseFlag=true;
	}
	public void restart(){
		pauseFlag=false;
	}
}

 

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

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

public class Game {

	//定义必要的变量
	private JFrame jf;
	private Graphics g;
	private Color color;
	
	//设置一个队列,保存所有的小球
	private ArrayList<Ball> list=new ArrayList<Ball>();
	
	//主函数
	public static void main(String args[]){
		new Game().initUI();
	}
	
	
	public void initUI(){
		//设置界面
		final JFrame jf=new JFrame();
		jf.setSize(400,400);
		jf.setDefaultCloseOperation(3);
		jf.setLocation(300,100);
		
		//设置一个布局
		jf.setLayout(new FlowLayout());
		
		//设置三个按钮,功能分别为添加、暂停、恢复线程。
		JButton btn1=new JButton("添加");
		JButton btn2=new JButton("暂停");
		JButton btn3=new JButton("恢复");
		
		jf.add(btn1);
		jf.add(btn2);
		jf.add(btn3);
		
		//设置时间监听器
		ActionListener l=new ActionListener(){

			
			public void actionPerformed(ActionEvent e) {
				
				//根据按钮上的文字来判断执行什么样的命令
				//先获取按钮上的文字
				String cmd=e.getActionCommand();
				
				//判断
				if("添加".equals(cmd)){
					//新建一个线程对象
					Ball bl=new Ball(jf,list);
					//启动线程
					bl.start();

					//把球保存在队列中
					list.add(bl);
					
				}
				
				if("暂停".equals(cmd)){
					//暂停线程,线程中有自己的暂停方法
					//遍历所有的小球,调用每一个小球的暂停方法
					for(int i=0;i<list.size();i++){
						
						Ball bl=list.get(i);
						//bl.suspend();//不建议使用
						//因为该方法不确定在线程中的哪一步暂停,不可控性,可以编写自己的暂停方法
						
						//自己的暂停方法
						bl.pause();
						
					}
				}
				
				if("恢复".equals(cmd)){
					//恢复线程的运行,线程中有自己的回复方法
					//同暂停一样,遍历所有小球,调用每一个小球的恢复方法
					for(int i=0;i<list.size();i++){
						
						Ball bl=list.get(i);
						//bl.resume();//不建议使用,原因同暂停方法一样
						
						//自己的恢复方法
						bl.restart();
					}
					
				}
				
				
			}
			
		};
		
		//在按钮上添加监听器
		btn1.addActionListener(l);
		btn2.addActionListener(l);
		btn3.addActionListener(l);
		
		//界面可视化
		jf.setVisible(true);
	}
	
}

   

 

  

遇到的问题:

1、我想在编写这类代码的时候,遇到最多的问题就是:我绘制的东西,为什么在界面上显示不出来。

我想,最有可能是你并没有传入相应的画布。

2、我在第一次画出运动的小球时,出现了小球频繁闪动的情况,

这有可能是休眠时间的原因,通过修改sleep的参数可以缓解频闪的情况。

 

代码的优化:

1、可以在类中设置一些私有属性作为变量,方便代码的修改。

2、在run方法中编写小球的擦除、移动、画的方法代码时,会使整个代码显得凌乱,可以将其设置为类中的方法,再在run方法中调用即可。

3、通过分形的思想,让小球显得更立体。

 

<!--EndFragment-->

分享到:
评论

相关推荐

    C#语言多线程初步理解

    总结来说,"C#语言多线程初步理解"这个主题涵盖了线程创建、线程安全、线程同步以及在WinForm应用中的实践。通过深入学习和实践这些概念,开发者可以编写出更加高效和响应迅速的应用程序。在“WindowsForms...

    用C#实现的unity线程初步

    在Unity引擎中,C#是主要的编程语言,用于创建游戏逻辑和其他交互式应用程序。本文将探讨如何在Unity中使用C#实现线程的基本概念...理解这些概念并适当应用它们,可以帮助你在Unity项目中实现高效且稳定的多线程代码。

    Windows多线程多任务设计初步

    ### Windows多线程多任务设计初步 在现代操作系统中,多线程和多任务处理是提升软件性能和响应性的关键。Windows操作系统通过支持多线程和多任务,使得多个应用程序和进程能够并行运行,提高了系统的整体效率。下面...

    线程监控工具,查找异常线程

    在Linux环境下,`top`、`htop`命令可以初步查看线程,而`gdb`、`strace`、`jstack`等工具则能进行深度分析。Java开发者可以使用JDK自带的`jconsole`、`jvisualvm`或第三方工具如`VisualVM`进行Java应用的线程监控。 ...

    Windows的多线程程序设计初步

    在实际应用中,通常需要使用临界区、信号量、事件、互斥量等机制来控制对共享资源的访问,以确保线程安全。此外,线程的生命周期管理也很重要,包括创建、暂停、恢复、终止以及资源的释放。 总的来说,不使用MFC...

    一个初步的用VB写“多线程”程序的例子(9KB)

    标题中的“一个初步的用VB写“多线程”程序的例子”表明这是一个关于使用Visual Basic (VB)编程语言实现多线程技术的示例项目。多线程是计算机编程中的一个重要概念,允许程序同时执行多个任务,从而提高效率和响应...

    CVI对多线程的如何支持

    举例来说,如果一个监控程序需要实时采集和处理数据,那么辅助线程可以负责数据的采集和初步处理工作,处理完毕的数据可以放入线程安全队列中,主线程再从队列中取出数据进行显示。在这个过程中,主线程可以通过...

    线程应用,主要展示初学者同步线程的认识与介绍

    附件为视频案例。主要对初学者展示了同步线程的认识 案例介绍了兔子与乌龟赛跑的同步线程,比较形象的介绍了大家对线程的初步理解

    多线程同步利用ccriticalsection

    通过以上讲解,你应该对MFC中的`CCriticalSection`有了初步了解,并能开始在自己的多线程项目中运用这一同步机制。不过,记住,线程同步是一个复杂的话题,需要结合具体场景灵活运用,避免潜在的并发问题。

    初步理解进程、线程关系

    在标题和描述中提到的线程应用,我们可以看到,程序运行时默认会开启一个主线程,它是程序执行的起点。如果在主线程中定义了子线程,那么子线程的执行并不一定是立即开始的,而是会在调用`start()`方法后才开始。在...

    VC多线程实现图像采集

    多线程技术作为提高程序执行效率的有效手段,在图像采集系统的开发中得到了广泛的应用。以下是对“VC多线程实现图像采集”这一主题的深入分析,涵盖了标题和描述中的关键知识点。 #### 图像采集技术概述 图像采集...

    基于ThreadX与ARM平台实时嵌入式多线程应用详解

    内容概要:本书详细介绍了利用ThreadX实时操作系统(RTOS)和ARM处理器架构进行嵌入式系统中多线程应用程序的设计与实现方法。涵盖了基本的数据类型定义、内存管理机制如内存块池、消息队列以及互斥量的应用,线程...

    多线程数据采集器源码

    【标题】"多线程数据采集器源码"所涉及的知识点主要集中在C#编程语言以及多线程技术的应用上。C#是微软开发的一种面向对象的编程语言,广泛用于Windows平台上的应用开发,尤其是与.NET框架结合时,能够提供高效、...

    VB.Net多线程的入门实例

    在VB.Net编程中,多线程是一...通过以上介绍,你应该对VB.Net中的多线程有了初步的理解。在实际项目中,根据需求合理地运用多线程技术,可以提高程序的性能和用户体验。不断实践和学习,你将更加熟练地掌握多线程编程。

    多线程游戏-snake贪吃蛇

    8. **测试与优化**:完成初步实现后,我们需要进行大量的测试,确保游戏在各种情况下都能正常运行,没有死锁、活锁或饥饿等问题。此外,还可以通过优化线程调度和内存管理来提升游戏的性能。 总结,编写一个多线程...

    .net开发人员面试题-多线程.pdf

    多线程的应用场景 1. 数据处理:多线程可以用于数据处理,例如数据压缩和加密。 2. 网络编程:多线程可以用于网络编程,例如服务器和客户端的开发。 3. 图形处理:多线程可以用于图形处理,例如图像和视频处理。 ...

    socket linux c++ 多线程3

    在IT领域,网络编程是不可...总的来说,结合C++、Linux和多线程的Socket编程,可以构建出强大的网络服务应用。通过学习和实践提供的各种资源,开发者能够掌握创建高效、稳定、并发的Socket服务器的关键技术和最佳实践。

    多线程起步,总结的,一些方法的使用,简单的列子,使用方法

    多线程是指在一个应用程序中同时执行多个独立的执行路径,每个路径称为一个线程。通过多线程,可以充分利用多核处理器资源,提高程序的执行效率和响应速度。在Java中,线程是轻量级的进程,它们共享同一内存空间,但...

    delphi线程入门示例

    通过以上讲解,你已经对Delphi中的线程编程有了初步了解。实践中,你可以创建一个简单的项目,包含一个`TThread`子类,并观察其运行效果,逐步加深理解。记住,线程虽好,但过度使用可能会导致复杂性和维护成本上升...

Global site tag (gtag.js) - Google Analytics