今天分享一个关于线程的编写以及制作一个简单的小动态。
首先了解并区分一下进程/线程 并发/并行这几个概念:
进程:在操作系统中,进程是程序的一次动态执行过程。它不仅仅是一个静态的程序代码,还包括程序在执行时所涉及的数据和资源。更精确地说,进程是一个具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的独立单位。
线程:线程是操作系统中程序执行的基本单位,是进程内部的独立执行路径。每个进程至少包含一个线程,称为“主线程”,而一个进程可以拥有多个线程,这些线程共享进程的资源,如内存地址空间和文件句柄。线程是操作系统进行任务调度的最小单位,它比进程更轻量,创建和销毁的开销更小,因此在多任务处理中被广泛使用。
线程与进程的主要区别在于:进程是资源分配的基本单位,而线程是调度的基本单位。多个线程可以共享同一个进程的资源,而进程之间是相互隔离的。这种资源共享机制使得线程之间的通信和数据交换更加高效。
并发:并发(Concurrency)是指多个任务在重叠的时间段内执行的能力,而不是严格意义上的“同时执行”。在计算机科学中,并发指的是系统能够处理多个任务的能力,这些任务可能交替执行、部分重叠执行,而不是完全同一时间运行。并发通常用于提高资源利用率和系统响应速度,特别是在多线程编程中。
并行:并行(Parallelism)是指多个任务或操作同时执行的计算方式。与并发不同,并行强调的是真正的同时性,而不是任务在时间上的重叠执行。并行计算通常依赖于多核处理器、分布式计算系统或多台计算机组成的集群,通过多个计算单元同时处理不同的任务或数据片段,以提升整体计算效率。
并发与并行的区别:并发是指多个任务在重叠的时间段内交替执行,可能并不真正同时运行,而是由操作系统调度轮流使用CPU资源。并发的目标是提高系统的响应能力和资源利用率。而并行则是多个任务在同一时刻执行,依赖于多核或分布式硬件支持,目标是提高整体计算速度和吞吐量。
现在开始写一个线程基础的小项目:
创建一个ThreadUI类,用于创建窗体添加面板以及运行程序。因为是在窗体添加的面板上体现动态,所以要在面板上获取画笔,再添加鼠标点击监听器(如下个类)。
import javax.swing.*;
import java.awt.*;
/**
* 线程游戏
*/
public class ThreadUI {
public void initUI(){
JFrame jf = new JFrame();
jf.setSize(900,900);
jf.setTitle("线程游戏");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setLocationRelativeTo( null);
//面板对象
JPanel jPanel = new JPanel();
jPanel.setBackground(Color.WHITE);
jf.add(jPanel,BorderLayout.CENTER);
jf.setVisible(true);
//获取面板上的画笔对象
Graphics g = jPanel.getGraphics();
//监听器
ThreadListener listener = new ThreadListener(g);
jPanel.addMouseListener(listener);
}
public static void main(String[] args) {
ThreadUI threadUI = new ThreadUI();
threadUI.initUI();
}
}
创建一个监听器类ThreadListener类,用于监听事件。extends MouseAdapter比implements MouseListener更优化的点在于,extends MouseAdapter不需要重写MouseListener中的所有代码,可以简化程序。将画笔引用传递到监听器中,在点击类中定义坐标位置x,y。然后创建线程对象tb,通过tb.start()启动线程,(每个线程只能启动一次)
start()方法:这个方法是开启一个线程的方法,首先给出start的源码:
public void start() {
synchronized (this) {
// 0 状态标志 意味着时"NEW"状态
if (holder.threadStatus != 0)
throw new IllegalThreadStateException();
start0();
}
}
我们看到需要判断 threadStatus是否位0 才能调用start0()这个本地方法 不等于0直接就会抛出异常 . start0()方法 就是本地方法 用来真正的创建并启动线程的方法. start()方法在第一次调用完start()方法后threadStatus 的值会发生改变不等于0了,致使下一次调用时start()方法直接就抛出异常。
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
/**
* 鼠标监听器-时间处理类
*/
public class ThreadListener extends MouseAdapter {
//画笔:引用传递
public Graphics g;
//构造方法初始化属性
public ThreadListener(Graphics g){
this.g = g;
}
public void mouseClicked(MouseEvent e){
//获取坐标
int x = e.getX();
int y = e.getY();
//创建线程对象
ThreadBall tb = new ThreadBall(g,x,y);
//启动线程,每个线程只能启动一次
tb.start();
}
}
创建一个ThreadBall类,这是一个自定义线程类,首先将画笔,坐标引入,再调用run()方法,通过sleep()方法控制小球运动的速度,设置黑球运动。run()方法是由主线程来执行,不能直接调用run()方法,必须先调用start()方法,因为run方法不会开辟新的栈空间。
import java.awt.*;
/**
* 自定义线程类:java.lang自动导入
*/
public class ThreadBall extends Thread{
public Graphics g;//画笔
public int x,y;//坐标
//初始化属性
public ThreadBall(Graphics g,int x,int y){
this.g = g;
this.x = x;
this.y = y;
}
//run 方法是重启线程后自动执行的方法
//该方法执行完,当前线程的所有资源被收回(不能再次启动)
public void run(){
System.out.println(Thread.currentThread().getName()+"线程启动");
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//自动运动的对象
g.setColor(Color.WHITE);
g.fillOval(x++,y,50,50);//绘制一个圆形
g.setColor(Color.BLACK);
g.fillOval(x,y,50,50);
}
}
}