先看一下效果:
一、简单介绍
以下介绍来自csdn博主菜鸟LV1的文章
1.进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。
2.线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。
一个程序至少一个进程,一个进程至少一个线程。
①地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
②资源拥有:同一进程内的线程共享本进程的资源,但是进程之间的资源是独立的。
③一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
④进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程。
⑤执行过程:每个独立的进程程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
⑥线程是处理器调度的基本单位,但是进程不是。
⑦两者均可并发执行。
3.优缺点:
①线程执行开销小,但是不利于资源的管理和保护。线程适合在SMP机器(双CPU系统)上运行。
②进程执行开销大,但是能够很好的进行资源管理和保护。进程可以跨机器前移。
4.什么地方用到多线程?
①后台线程:比如定期执行一些特殊任务,如定期更新配置文件,任务调度,一些监控用于定期信息采集等。
②最典型的应用比如tomcat,tomcat内部采用的就是多线程,上百个客户端访问同一个web应用,tomcat接入后都是把后续的处理扔给一个新的线程来处理,这个新的线程最后调用到我们的servlet程序,比如doGet或者doPost方法。还有就是需要异步处理的时候,需要使用多线程。
③特别耗时的操作,如备份数据库,可以开个线程执行备份,然后执行返回,前台不断向后台询问线程执行状态。
5.多线程实现方法
①继承Thread类
②实现Runnable接口
一个类如果实现了Runnable接口或者继承了Thread类,那么它就是一个多线程类,如果是要实现多线程,还需要重写run()方法,所以run() 方法是多线程的入口。
区别:
- Thread是Runnable接口的子类,实现Runnable接口的方式解决了Java单继承的局限
- Runnable接口实现多线程比继承Thread类更加能描述数据共享的概念
6.多线程有几种实现同步方法?
同步的实现方面有两种,分别是synchronized,wait与notify
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
7.产生死锁的原因?
产生死锁的四个必要条件:
1、互斥条件:一个资源每次只能被一个进程使用。
2、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3、不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
避免死锁
上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件,就可以避免死锁发生,一般有以下几种方法:
1、按同一顺序访问对象。
2、避免事务中的用户交互。
3、保持事务简短并处于一个批处理中。
4、使用较低的隔离级别。
5、使用基于行版本控制的隔离级别。
6、使用绑定连接。
二、项目示实例
1.功能:
①界面
②界面上运动的小球
③小球间相互独立,小球之间、小球与界面边界碰撞满足动量定理
2.基本框架:
- 小球彼此之间相互独立,它们的活动是同时发生的
- 小球之间会发生碰撞,需要互相知晓彼此的位置信息,因此共享一份信息
- Frame上运动着多个ball,Frame管理它们的活动和信息
- 可将Frame作为进程,每产生一个小球,生成一个线程来进行管理。
因此Frame是爸爸(进程),家里每多一个宝宝(ball),爸爸就请一个保姆(线程)来看管,爸爸会给保姆安排一些基本的工作和对宝宝的各种活动的处理方式,爸爸掌握着所有宝宝的活动和情况和信息。
3.具体实现
①宝宝(ball)的基本活动和信息
信息:颜色,运动方向,速度,位置
活动:运动,在爸爸(进程)的房子里(Frame)按照速度方向、大小运动
②管家(run)处理的事件和方式
在接收到爸爸(进程)的start()命令后,开始和宝宝活动,接收到爸爸的暂停命令后,立即阻止宝宝的运动(stop)
宝宝(ball)可能撞到墙上(边界),还可能和别的宝宝(ball)撞到一起,聪明的管家根据动量守恒定律改变宝宝的运动状态。
4.代码实现
①Ball类:
- 构造函数内颜色和大小随机生成
- 在给定的位置开始运动,速度随机生成
- 小球需要画出自己,要制造出运动的效果,联想到动画片,就是利用视觉暂留将一张一张的图片连续展示的结果:
①我们必须让小球不断变化位置,每次用给定的颜色画小球之前用背景颜色把之前的小球擦除,保证任何时刻一个Ball只显示一个球的图像
②在每一个图像上需要停留一段时间,这里设置的是100ms,用的是sleep函数让线程休眠。
- 小球要有判断自己是否撞到边界或者其他的球的方法,并且在这些方法里面做出响应反应
①如果撞到边界,让相应方向的“速度”(dx/dy)反向。
②如果撞到其他小球,交换两个小球的速度(如果严格按照动量守恒定律,速度的变化会要复杂一些)。
- 每个小球地运动是独立地,因此我们在这里实现Runnable接口,运用线程来实现,那么就需要重写它里面的run函数
- 最重要的一个函数,是run函数,线程启动后执行的就是run方法,需要并行实现独立的功能必须在run里面实现或者呗调用:
小球独立地运动,因此重复进行三个过程:①位置移动 ②判断撞击的情况并作出处理 ③画出小球 ④暂留一段时间后擦去小球
package Ball_thread_5_20;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
public class Ball implements Runnable{
private Ball []ball_array= new Ball[100];//引用进程的100个线程
public int num,width,height,x,y,dx,dy;
private Color color;
private int r;
private Graphics g;//画布
Random random=new Random();
//重写run函数,运动
public void run(){
while(true){
x+=dx;y+=dy;
//撞到边界
overEdge();
//与其他小球碰撞
Ball temp;
for(int i=0;i<num;i++){
temp=ball_array[i];
if(temp!=this){
temp.crash(this);
}
}
this.drawBall();
this.cleanBall();
}
}
//构造函数
public Ball(int x,int y,int r,int w,int h){
this.x=x;
this.y=y;
this.r=random.nextInt(r)+20;
//界面的高度和宽度
width=w;
height=h;
//随机生成速度向量
dx=random.nextInt(60)-30;
dy=random.nextInt(60)-30;
//随机生成颜色
int c1,c2,c3;
c1=random.nextInt(255);
c2=random.nextInt(255);
c3=random.nextInt(255);
color=new Color(c1,c2,c3);
}
//获得画布
public void setg(Graphics g){
this.g=g;
}
//获得小球数组
public void setBalls(Ball[] b,int num){
ball_array=b;
this.num=num;
}
//判断是否超出边界
public void overEdge(){
if(x+r>width){
x=width-r;
dx=-dx;
}
if(y+r>height){
y=height-r;
dy=-dy;
}
if(x-r<0){
x=r;
dx=-dx;
}
if(y-r<0){
y=r;
dy=-dy;
}
}
//判断是否和x,y处的球相撞
public void crash(Ball b){
int i=b.x,j=b.y;
if((i-x)*(i-x)+(j-y)*(j-y)<=r*r){
int temp1=b.dx,temp2=b.dy;
b.dx=this.dx;this.dx=temp1;
b.dy=this.dy;this.dy=temp2;
if(b.x<this.x) b.x=this.x-2*r;
else b.x=this.x+2*r;
if(b.y<this.y) b.y=this.y-2*r;
else b.y=this.y+2*r;
}
}
//画出小球
public void drawBall(){
g.setColor(color);
g.fillOval(x, y, r, r);
}
//擦除小球
public void cleanBall(){
//线程休眠100ms
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
g.setColor(new Color(240,240,240));
g.fillOval(x, y, r, r);
}
}
②BallUI类(界面):
- 界面类首先功能必须能展示一个界面,在init方法中初始化一个界面并获得画布(因为小球需要画出来)
- 我们要实现的是界面上在鼠标点击处生成一个小球,所以需要监听鼠标点击事件,(在初始化界面的时候需要添加鼠标监听器也就是this),实现MouseListner接口,重写其中的Mouseclicked函数来处理鼠标点击事件:
①得到点击位置并生成小球,将小球加入集合,将界面的画布传给小球,以便画画
②创建线程Thread对象,启动线程
package Ball_thread_5_20;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
public class BallUI extends JFrame implements MouseListener{
/**
* 继承JFrame,实现鼠标监听器接口
*/
private static final long serialVersionUID = 1L;
private Ball []ball_array= new Ball[100];//最多100个ball
private Graphics g ;//画布
private BallUI UI;//窗口对象
private int num,r=20;//小球个数
//主函数
public static void main(String[] args){
BallUI bui=new BallUI();
bui.init();
}
@Override
public void paint(Graphics g) {
super.paint(g);
// drawBall(g);
// moveBall();
}
//显示界面
public void init(){
num=0;
UI=this;
this.setTitle("willow's balls~");
this.getContentPane().setBackground(new Color(240,240,240));
this.setSize(1000, 600);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
//添加监听器
this.addMouseListener(this);
g=this.getGraphics();//获得画布
}
//实现接口函数鼠标点击,响应点击事件
@Override
public void mouseClicked(MouseEvent arg0) {
//在(x,y)生成新的小球并加入小球集合ball_array
int x=arg0.getX(),y=arg0.getY();
Ball b=new Ball(x,y,r,UI.getWidth(),UI.getHeight());
b.setBalls(ball_array,num);
b.setg(g);
ball_array[num++]=b;
//启动小球线程
Thread t = new Thread(b);
t.start();
}
@Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}