//线程初步应用——————————————————————————————————
最近一段时间,我学习了有关线程的知识,今天来总结一下最近学到的知识。
我们的任务是要在窗体上绘制出一些能够互相碰撞的小球。这个功能的实现,需要用到线程相关知识。线程——进程中的某一个独立的运行单位。而多线程,是指几个线程同时进行,你在操作一个线程的时候,计算机自己运行其他的线程。(把多的任务交给计算机来做)。
在做的过程中分为了几部分
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#语言多线程初步理解"这个主题涵盖了线程创建、线程安全、线程同步以及在WinForm应用中的实践。通过深入学习和实践这些概念,开发者可以编写出更加高效和响应迅速的应用程序。在“WindowsForms...
在Unity引擎中,C#是主要的编程语言,用于创建游戏逻辑和其他交互式应用程序。本文将探讨如何在Unity中使用C#实现线程的基本概念...理解这些概念并适当应用它们,可以帮助你在Unity项目中实现高效且稳定的多线程代码。
### Windows多线程多任务设计初步 在现代操作系统中,多线程和多任务处理是提升软件性能和响应性的关键。Windows操作系统通过支持多线程和多任务,使得多个应用程序和进程能够并行运行,提高了系统的整体效率。下面...
在Linux环境下,`top`、`htop`命令可以初步查看线程,而`gdb`、`strace`、`jstack`等工具则能进行深度分析。Java开发者可以使用JDK自带的`jconsole`、`jvisualvm`或第三方工具如`VisualVM`进行Java应用的线程监控。 ...
在实际应用中,通常需要使用临界区、信号量、事件、互斥量等机制来控制对共享资源的访问,以确保线程安全。此外,线程的生命周期管理也很重要,包括创建、暂停、恢复、终止以及资源的释放。 总的来说,不使用MFC...
标题中的“一个初步的用VB写“多线程”程序的例子”表明这是一个关于使用Visual Basic (VB)编程语言实现多线程技术的示例项目。多线程是计算机编程中的一个重要概念,允许程序同时执行多个任务,从而提高效率和响应...
举例来说,如果一个监控程序需要实时采集和处理数据,那么辅助线程可以负责数据的采集和初步处理工作,处理完毕的数据可以放入线程安全队列中,主线程再从队列中取出数据进行显示。在这个过程中,主线程可以通过...
附件为视频案例。主要对初学者展示了同步线程的认识 案例介绍了兔子与乌龟赛跑的同步线程,比较形象的介绍了大家对线程的初步理解
通过以上讲解,你应该对MFC中的`CCriticalSection`有了初步了解,并能开始在自己的多线程项目中运用这一同步机制。不过,记住,线程同步是一个复杂的话题,需要结合具体场景灵活运用,避免潜在的并发问题。
在标题和描述中提到的线程应用,我们可以看到,程序运行时默认会开启一个主线程,它是程序执行的起点。如果在主线程中定义了子线程,那么子线程的执行并不一定是立即开始的,而是会在调用`start()`方法后才开始。在...
多线程技术作为提高程序执行效率的有效手段,在图像采集系统的开发中得到了广泛的应用。以下是对“VC多线程实现图像采集”这一主题的深入分析,涵盖了标题和描述中的关键知识点。 #### 图像采集技术概述 图像采集...
内容概要:本书详细介绍了利用ThreadX实时操作系统(RTOS)和ARM处理器架构进行嵌入式系统中多线程应用程序的设计与实现方法。涵盖了基本的数据类型定义、内存管理机制如内存块池、消息队列以及互斥量的应用,线程...
【标题】"多线程数据采集器源码"所涉及的知识点主要集中在C#编程语言以及多线程技术的应用上。C#是微软开发的一种面向对象的编程语言,广泛用于Windows平台上的应用开发,尤其是与.NET框架结合时,能够提供高效、...
在VB.Net编程中,多线程是一...通过以上介绍,你应该对VB.Net中的多线程有了初步的理解。在实际项目中,根据需求合理地运用多线程技术,可以提高程序的性能和用户体验。不断实践和学习,你将更加熟练地掌握多线程编程。
8. **测试与优化**:完成初步实现后,我们需要进行大量的测试,确保游戏在各种情况下都能正常运行,没有死锁、活锁或饥饿等问题。此外,还可以通过优化线程调度和内存管理来提升游戏的性能。 总结,编写一个多线程...
多线程的应用场景 1. 数据处理:多线程可以用于数据处理,例如数据压缩和加密。 2. 网络编程:多线程可以用于网络编程,例如服务器和客户端的开发。 3. 图形处理:多线程可以用于图形处理,例如图像和视频处理。 ...
在IT领域,网络编程是不可...总的来说,结合C++、Linux和多线程的Socket编程,可以构建出强大的网络服务应用。通过学习和实践提供的各种资源,开发者能够掌握创建高效、稳定、并发的Socket服务器的关键技术和最佳实践。
多线程是指在一个应用程序中同时执行多个独立的执行路径,每个路径称为一个线程。通过多线程,可以充分利用多核处理器资源,提高程序的执行效率和响应速度。在Java中,线程是轻量级的进程,它们共享同一内存空间,但...
通过以上讲解,你已经对Delphi中的线程编程有了初步了解。实践中,你可以创建一个简单的项目,包含一个`TThread`子类,并观察其运行效果,逐步加深理解。记住,线程虽好,但过度使用可能会导致复杂性和维护成本上升...