多线程的概念和JAVA实现

多线程编程,相信大家或多或少都有听到过。以前对这个概念的理解比较模糊,最近写了一个简单的JAVA程序来帮助自己理解这个概念。

一、什么是程序?什么是进程?什么是线程?

A.程序:程序是一个指令序列。执行某一个进程所需要的所有文件,这些文件最后都会被翻译成指令序列。

B.进程:进程是正在运行的程序的实例。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。

C.线程:线程是程序中一个单一的顺序控制流程。进程内有一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指令运行时的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

二、这三者之间的区别

    程序和进程之间的区别

A.进程是动态的,而程序是静态的。

B.进程有一定的生命期,而程序是指令的集合,不存在生命期。

C.1个程序可以对应多个进程,但1个进程只能对应1个程序。

我们可以把程序看做剧本,进程看做演出。也就是一个剧本可以同时进行多场演出,但是每场演出只有一个剧本。

    进程和线程的区别

A.地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。

B.通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信。

C.调度和切换:线程上下文切换比进程上下文切换要快得多。

D.在多线程OS中,线程不是一个可执行的实体。

三、为何需要多线程编程

一个主要的原因就是为了充分利用CPU资源,提高运行速率。线程在CPU里面是并发执行的。如果第一个线程执行到了一些需要长时间等待的操作时,PC并不会一直等待这个操作结束,而是控制转移给另一个线程,去执行它。等到第一个线程的等待时间结束后再重新调回第一个线程,执行它。这样子我们就可以大大减少CPU的闲置时间。

四、JAVA实现线程的方式

Runnable  接口:里面带有run方法,也就是线程的执行方法

Thread  类,该类实现Runnable接口:A.run方法;B.start线程的启动方法。这两个是最重要的方法,其他的还有一些比如sleep等等。

现成的启动必须要靠start方法,如果你用的是Runnable接口,那么你必须额外定义一个Thread类对象,利用它的start方法来启动线程。如果你用的是Thread类,那么直接调用对象的start方法即可。

五、程序实例——小球运动

在界面实现多个自由运动的小球。单线程编程的话只能实现一个运动的小球,因为每一个运动的小球都是不会停止的,它会一直占用一个线程。

代码部分如下:

//实现球运动的界面类BallFrame

import javax.swing.JFrame;

public class BallFrame extends JFrame{
	
	static public void main(String[] args) {
		BallFrame jf=new BallFrame();
		jf.initUI();
	}
	
	public void initUI() {
		this.setTitle("小球运动");
		this.setSize(800, 600);
		this.setDefaultCloseOperation(3);
		this.setLocationRelativeTo(null);
		this.setResizable(false);
		
		this.setVisible(true);
		
		//事件监听机制的画笔必须等到画板画完了才能取
		frameListener fl=new frameListener(this);
		this.addMouseListener(fl);
	}
}
//对界面设置监听机制

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;

public class frameListener implements MouseListener{
	public BallFrame bf;
	public int x;
	public int y;
	
	
	public frameListener(BallFrame bf) {
		this.bf=bf;
	}
	
	@Override
	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub
		x=e.getX();
		y=e.getY();
		
		Ball ball=new Ball(x,y,Color.blue,bf);
		
		//如果直接调用了run,那么程序不会新建一个线程,而只会在当前线程中运行
		//这使得run只会被当做一个普通方法
		//ball.run();
		
		//继承Thread,启动球类对象的线程
		//ball.start();
		
		//实现Runable接口,需要把实现的接口类对象传给Thread,再由Thread去调用start方法
		Thread t=new Thread(ball);
		t.start();
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {		
		
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		
	}

	@Override
	public void mousePressed(MouseEvent arg0) {
		
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		
	}

}
//实现一个球类,继承Thread类,或者实现Runnable接口

import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

//public class Ball extends Thread{
public class Ball implements Runnable{
	public int x;
	public int y;
	public BallFrame bf;
	public Graphics g;
	public Color color;
	public double speedx=0;
	public double speedy=0;
	
	public Ball(int x,int y,Color color,BallFrame bf) {
		this.x=x;
		this.y=y;
		this.color=color;
		this.bf=bf;
		this.g=bf.getGraphics();
	}
	
	public void Speed() {
		//随机获取小球的移动方向
		//先随机获取小球xy的移动速度
		int dadii=5;//半径长
		Random random=new Random();
		speedx=random.nextInt(6);
		double computer=dadii*dadii-speedx*speedx;
		speedy=Math.sqrt(computer);
		
		//随机把小球xy移动速度的值设置为正负
		int choosex=random.nextInt(2);
		if(choosex==0) speedx*=-1;
		int choosey=random.nextInt(2);
		if(choosey==0) speedy*=-1;
		
		//判断当前位置是否越界
		if(y>bf.getHeight()) speedy*=-1;
		else if(y<0) speedy*=1;
		if(x>bf.getWidth()) speedx*=-1;
		else if(x<0) speedx*=1;	
	}
	
	//重写run方法
	public void run() {
		while(true) {
			g.setColor(color);
			g.drawOval(x, y, 20, 20);
			Speed();
			y+=speedy;
			x+=speedx;
			try {
				//sleep函数存在Thread中
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

六、总结

1.直接继承Thread类会比实现Runnable接口简洁一点,如果实现Runnable接口你还是要额外去实例化一个Thread类的对象。

2.线程之间不会互相干扰。我们这个程序每一个小球的运动都是不会结束,它们会一直在运行。当你每次点击界面时,进程都会帮你创建一个线程,去实现一个运动的小球。这些小球之间互不干扰。但是如果你把run方法当做一个普通方法来调用时,只有第一次点击界面时会生成一个运动的小球,接下去无论你怎么点击,界面都没有反应。因为你是在尝试往同一个线程里面生成多个小球,但是由于当前这个小球还没有运行结束,当前线程一直有指令需要执行,因此无法对你进行的鼠标点击操作做出相应的反应。




猜你喜欢

转载自blog.csdn.net/alexwym/article/details/80922478