一、进程与线程
进程就是计算机系统中正在运行的一个程序,可以看成是一个程序运行的实例。进程是系统资源分配的实体,每个进程都有系统分配的独立的地址空间,一个进程一般无法访问其它进程的数据结构,如果需要一个进程访问另一个进程的资源,需要建立进程间的通信,如嵌套字等。
线程是进程的一个实体,是进程的一条执行路径,是操作系统能够进行运算调度的最小单位。它被包含在进程之中。一个进程可以拥有多个线程,每个线程有其所属进程的栈空间。同一进程内的线程会共享部分状态,多个线程可以读写一块内存,同时线程还有自己的寄存器和栈,其它线程可以读写这些栈内存。当一个线程修改了进程的资源,它的兄弟线程会立刻看到这种变化。
进程和线程的具体区别:
- 地址空间和其它资源(如打开文件),进程之间是相互独立的,同一进程的各个线程是互相共享的。
- 通信:进程间通信IPC(管道,信号量,共享内存,消息队列),线程可以直接读写进程数据段来进行通信。
- 线程进行上下文切换时比进程快得多
需要频繁创建销毁的优先使用线程;因为对进程来说创建和销毁一个进程的代价是很大的。线程的切换速度快,所以在需要大量计算,切换频繁时使用线程,还有耗时的操作时用使用线程可提高应用程序的响应。因为对CPU系统的效率使用上线程更占优势,所以可能要发展到多机分布的用进程,多核分布用线程。
线程切换:
- cpu给线程分配时间片(也就是分配给线程的时间),执行完时间片后会切换都另一个线程。
- 切换之前会保存线程的状态,下次时间片再给这个线程时才能知道当前状态。
- 从保存线程A的状态再到切换到线程B时,重新加载线程B的状态的这个过程就叫上下文切换。
- 而上下切换时会消耗大量的cpu时间。
线程开销:
- 上下文切换消耗
- 线程创建和消亡的开销
- 线程需要保存维持线程本地栈,会消耗内存
CPU与进程线程之间的各种概念:
- 物理CPU数量,指实际插入主板的CPU数量,家用计算机一般只有一个CPU因为多个CPU主板会比较复杂,一般只会在服务器处理等工业领域用到。
- 核心(core)数:一开始,每个CPU上只有一个核心,对于操作系统而言,同时只能运行一个进程/线程,为了提高性能,CPU厂商开始在单个CPU上增加核心数量,如双核心的CPU就能同时运行两个进程/线程。
- 同时多线程技术:本质一样,是为了提高单个 core 同一时刻能够执行的多线程数的技术(充分利用单个 core 的计算能力,尽量让其“一刻也不得闲”)。本来一个核心只能运行一个进程/线程,运用同时多线程技术之后可以在一个core上运行多个线程/进程,例如AMD的线程撕裂者3995WX,64核心128线程,一个CPU就能同时运行128个进程/线程/
可以打开电脑的任务管理器来查看正在运行的进程和正在运行的线程数量:
二.进程与线程的生命周期(这里介绍java层面中线程的生命周期):
- 新建(new):当线程对象创建后,便进入了新建状态(如:Thread t = new MyThread();)。
- 就绪状态(runnable):当调用线程对象start方法时,线程便进入了就绪状态,只是线程做好了准备,随时等待CPU的执行,而并不是说调用了t.start()此线程就会立即执行。
- 运行状态(running):当CPU开始调度处于就绪状态的线程时,此线程才真正处于运行状态,得以真正的执行。就绪状态是进入运行状态的唯一途径,线程只要想进入运行状态,就必须线进入就绪状态之中。
- 阻塞状态(blocked)处于运行状态的线程由于某种原因(缺少资源 IO,锁等),暂时放弃对CPU的使用权,此时进入阻塞状态,停止执行,直到此线程重新进入就绪状态,才能重新被CPU执行。
- 死亡状态:线程执行完了run()方法,线程结束了其生命周期,进入死亡状态。
Ps:
等待状态:线程对象调用了wait()方法,JVM就会将线程方法置入等待池中
超时等待状态:调用Sleep(long time), join(long time)会时线程处于睡眠状态
几个状态之间的变化如图所示:
三、单线程与多线程
多线程就是一个程序运行的时候有多个线程在同时运行。
单线程与多线程程序的区别:
单线程:多个任务只能依次执行,上一个任务结束,下一个任务才能开始执行,就比如一个网吧里面只有一个座位,每次只能容纳一个人上网,一个人上机之后其它人需要排队等待,当这个人下机之后,下一个人才能上机。
多线程:一个网吧有很多座位,每次能容纳很多人上网。
程序运行原理:
- 分时调度:所有的线程会轮流平均分配CPU的使用权
- 抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
串行、并发、并行:
串行:多个任务,执行时一个执行完再执行另一个。
并发:多个线程在单个核心运行,同一时间一个线程运行,系统不停切换线程,看起来像同时运行,实际上是线程不停切换。
并行:每个线程分配给独立的核心,线程同时运行。
继承thread线程的Java代码实现:
public class T1 {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.run();
System.out.println("如果是直接执行run方法,肯定是按代码顺序执行的,因为是通过主线程调用的");
}
}
class MyThread extends Thread
{
public void run()
{
System.out.println("创建的线程");
}
}
通过实现runnable接口实现:
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("运行中!");
}
}
public class Run {
public static void main(String[] args) {
Runnable runnable=new MyRunnable();
Thread thread=new Thread(runnable);
thread.start();
System.out.println("运行结束!");
}
}
java线程开发小项目实例:
import javax.swing.*;
import java.awt.*;
public class UI {
public void initUI(){
JFrame jf = new JFrame();
jf.setTitle("多线程编程之小球碰撞移动");
jf.setSize(800,800);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setLocationRelativeTo(null);
jf.getContentPane().setBackground(Color.WHITE);
jf.setVisible(true);
ThreadListener listener = new ThreadListener(jf.getGraphics());
jf.addMouseListener(listener);
}
public static void main(String[] args) {
UI ui = new UI();
ui.initUI();
}
}
import java.awt.*;
import java.util.ArrayList;
import java.util.Random;
import java.util.List;
public class Ball {
private int x,y,speedX,speedY,size;
private List<Ball> balls = new ArrayList<>();
Random random = new Random();
public Ball(int x , int y){
this.x = x;
this.y = y;
speedX = random.nextInt(3,15);
speedY = random.nextInt(3,15);
size = random.nextInt(20,50);
}
public void drawBall(Graphics g){
g.setColor(Color.WHITE);
g.fillOval(x,y,size,size);
move();
g.setColor(Color.BLACK);
g.fillOval(x,y,size,size);
}
public void move(){
x += speedX;
y += speedY;
if(x+size/2 >= 800 || x < 0){
speedX = -speedX;
}
if(y+size/2 >= 800 || y < 0){
speedY = -speedY;
}
}
public void bang(Ball ball1 , List<Ball> balls){
for(int i = 0 ; i < balls.size() ; i++){
Ball ball2 = balls.get(i);
if(ball1.hashCode() == ball2.hashCode()){
continue;
}else{
if((ball1.size+ball2.size)/2 >= Math.sqrt((ball1.x- ball2.x)*(ball1.x- ball2.x)+(ball1.y-ball2.y)*(ball1.y- ball2.y))){
ball1.speedX = -ball1.speedX;
ball1.speedY = -ball1.speedY;
ball2.speedX = -ball2.speedX;
ball2.speedY = -ball2.speedY;
}
}
}
}
}
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
public class ThreadListener extends MouseAdapter {
private Graphics g;
private List<Ball> balls = new ArrayList<>();
private ThreadBall runnable = null;
public ThreadListener(Graphics g){
this.g = g;
if(runnable == null){
runnable = new ThreadBall(balls , g);
Thread t = new Thread(runnable);
t.start();
}
}
@Override
public void mouseClicked(MouseEvent e){
int x = e.getX();
int y = e.getY();
int key = e.getButton();
switch (key){
case 1:
Ball ball = new Ball(x,y);
balls.add(ball);
break;
case 3:
if(runnable.getFlag()){
runnable.setFlag(false);
}else{
runnable.setFlag(true);
}
break;
}
}
}
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
public class ThreadBall implements Runnable{
private Graphics g;
private boolean flag;
private List<Ball> balls = new ArrayList<>();
public ThreadBall(List<Ball> balls , Graphics g){
this.balls = balls;
this.g = g;
}
public void setFlag(boolean flag){
this.flag = flag;
}
public boolean getFlag(){
return flag;
}
public void run(){
while(true){
try{
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(flag){
continue;
}
for(int i = 0 ; i < balls.size() ; i++){
Ball ball = balls.get(i);
ball.drawBall(g);
ball.bang(ball , balls);
}
}
}
}