进程
执行中的程序叫做执行中的程序叫做进程(Process),是一个动态的概念。 为了使计算机程序得以运行,计算机需要加载代码,同 时也要加载数据。
进程是程序的一次动态执行过程, 占用特定的地址空 间。每个进程由3部分组成:cpu,data,code。每个进程都是独立的,保有自己的cpu时间,代码和数据,即便用同一份程序产生好几个进程,它们之间还是拥有自己的这3样东西。 多任务(Multitasking)操作系统将CPU时间动态地划分给每个进程,操作系统同时执行多个进程,每个进程独立运行。以进程的观点来看,它会以为自己独占 Cpu的使用权执行中的程序叫做进程(Process),是一个动态的概念。 为了使计算机程序得以运行,计算机需要加载代码,同时也要加载数据。
线程
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流, 一个进程中可以并发多个线程,每条线程并行执行不同的任务。
- 一个进程可拥有多个并行的(concurrent)线程
- 一个进程中的线程共享相同的内存单元/内存地址空 间可以访问相同的变量和对象,而且它们从同一堆中 分配对象通信、数据交换、同步操作
- 由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信 息传递的速度也更快。
程序是指令的集合,代码的集合;而进程是动态的概念,当程序在执行时,系统分配进程;多线程是在同一 进程下,充分利用资源 ,多条执行路径,共享资源 (cpu data code)。
区别 | 进程 | 线程 |
---|---|---|
根本区别 | 作为资源分配的单位 | 调度和执行的单位 |
开销 | 每个进程都有独立的 代码和数据空间(进 程上下文),进程间 的切换会有较大的开 销 | 线程可以看做轻量级的 进程,同一类线程共享 代码和数据空间,每个 线程有独立运行栈和程 序计数器(PC),线程 切换的开销小 |
所处环境 | 在操作系统中能同时 运行多个任务(程 序) | 在同一应用程序中有多个顺序流同时执行 |
分配内存 | 系统在运行的时候会 为每个进程分配不同 的内存区域 | 线程间共享进程的所有 资源,每个线程只有有 自己的堆栈和局部变 量。线程由CPU独立调 度执行,在多CPU环境 下就允许多个线程同时 运行 |
包 含 关 系 | 没有线程的进程可以看作单线程,如果一 个进程拥有多个线程,则执行过程不是 一条线的,而是多条线(线程)共同完成的 | 线程是进程的一部分,所以线程有的时候会被称为是轻量级进程或轻权进程 |
多线程的优点:
- 资源利用率更好;
- 程序设计在某些情况下更简单;
- 程序响应更快
多线程的缺点:
- 设计更复杂,虽然有一些多线程应用程序比单线程的 应用程序要简单,但其他的一般都更复杂。在多线程访问共享数据的时候,这部分代码需要特别的注意。 线程之间的交互往往非常复杂。不正确的线程同步产生的错误非常难以被发现,并且重现以修复。
- 上下文切换的开销 当 CPU 从执行一个线程切换到执 行另外一个线程的时候,它需要 先存储当前线程的本 地的数据,程序 指针等,然后载入另一个线程的本地 数据,程序指针 等,最后才开始执行。这种切换称 为“上下文切 换”(“context switch”)。CPU 会在一 个上 下文中执行一个线程,然后切换到另外一个上下文中 执 行另外一个线程。上下文切换 并不廉价。如果没 有必要,应该减少上下文切换的发生。
线程的五种状态:
- 新建状态
- 就绪状态
- 运行状态
- 阻塞状态
- 死亡状态
- 新建状态: 使用 new 关键字和 Thread 类或其子类建立一个线程 对象后,该线程对象就处于新建状态。它保持这个状 态直到程序 start() 这个线程。
- 就绪状态:当线程对象调用了start()方法之后,该线程就进入就 绪状态。就绪状态的线程处于就绪队列中,要等待 JVM里线程调度器的调度。
- 运行状态: 如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线 程最为复杂,它可以变为阻塞状态、就绪状态和死亡 状态。
- 阻塞状态:如果一个线程执行了sleep(睡眠)、suspend(挂 起)等方法,失去所占用资源之后,该线程就从运行 状态进入阻塞状态。在睡眠时间已到或获得设备资源 后可以重新进入就绪状态。可以分为三种:
- 等待阻塞:运行状态中的线程执行 wait() 方法, 使线程进入到等待阻塞状态。
- 同步阻塞:线程在获取 synchronized同步锁失败 (因为同步锁被其他线程占用)。
- 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O请求时,线程就会进入到阻塞状态。当 sleep() 状态超时,join() 等待线程终止或超时,或 者 I/O 处理完毕,线程重新转入就绪状态。
- 死亡状态: 一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
使线程进入阻塞状态的方法:
- sleep方法:sleep() 方法需要指定等待的时间,它可 以让当前正在执行的线程在指定的时间内暂停执行, 进入阻塞状态,该方法既可以让其他同优先级或者高 优先级的线程得到执行的机会,也可以让低优先级的 线程得到执行机会。但是 sleep() 方法不会释放“锁标志”,也就是说如果有 synchronized 同步块,其他线 程仍然不能访问共享数据。
- yield方法: yield() 方法和 sleep() 方法类似,也不会释 放“锁标志”,区别在于它没有参数,即 yield() 方法 只是使当前线程重新回到可执行状态,所以执行 yield() 的线程有可能在进入到可执行状态后马上又被执行。让出CPU的使用权,从运行态直接进入就绪态。让CPU重新挑选哪一个线程进入运行状态。
- join方法: 方法会使当前线程等待调用 join() 方法的线程执行结束之后,才会继续往后执行
join方法的实例:
package com.ysh.demo.review04;
public class DohomeworkDemoTest01 {
public static void main(String[] args) {
new Thread(new Teacher()).start();
}
}
class Teacher implements Runnable{
@Override
public void run() {
System.out.println("老师来了,开始上课");
System.out.println("今天开始测试");
System.out.println("测试的内容是..........");
System.out.println("测试时间是120分钟");
Thread thread=new Thread(new Student());
thread.start(); //开启学生线程
try {
thread.join(); //插队
//如果join()方法里面没有参数,表示永远都会等待另一个线程结束完,然后再执行其它线程,但是如果加入参数的话,则表示thread线程只能插队传入的参数毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("还有30分钟");
System.out.println("考试结束");
}
}
class Student implements Runnable{
@Override
public void run() {
System.out.println("学生拿到试卷,开始答题");
System.out.println("学生抓脑");
System.out.println("唉,考试题目真变态");
}
}
yield的实例:
package com.ysh.demo.review04;
public class ThreadYieldTest implements Runnable{
public static void main(String[] args) {
ThreadYieldTest tyl=new ThreadYieldTest();
Thread th=new Thread(tyl,"我是A");
Thread th2=new Thread(tyl,"我是B");
th.start();
Thread.yield();
th2.start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"哈哈哈");
System.out.println(Thread.currentThread().getName()+"噢噢噢噢");
}
}
Thread中常用方法:
常用方法 | 功能 |
---|---|
isAlive() | 判断线程是否还"活"着,即线程是否还未终止 |
getPriority() | 获得线程的优先级数值 |
setPriority | 设置线程的优先级数值(线程的优先级是1-10) |
setName() | 给线程设置名字 |
getName() | 获取线程的名字 |
currentThread()(静态方法) | 获取当前正在运行的线程对象 |
Thread线程中的三个常量:
MIN_PRIORITY | MAX_PRIORITY | NORM_PRIORITY |
---|---|---|
1 | 10 | 5 |
线程同步(synchronized):
线程同步:即当有一个线程在对内存进行操作时,其他 线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态。
同步就是协同步调,按预定的先后次序进行运行。如: 你说完,我再说。
死锁:
死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的线程称为死锁线程。
//口红
class Lipstick{
}
// 镜子
class Mirror{
}
class Makeup extends Thread {
int flag;
String girl;
static Lipstick lipstick=new Lipstick();
static Mirror mirror= new Mirror();
@Override
public void run() {
// TODO Auto-generated method stub
doMakeup();
}
void doMakeup(){
if(flag==0){
synchronized (mirror) {
System.out.println(girl+"拿着镜 子!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lipstick) {
System.out.println(girl+"拿着口红!");
}
}
}else{
synchronized (lipstick) {
System.out.println(girl + "拿着口 红!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (mirror) {
System.out.println(girl + "拿着镜子!");
}
}
}
}
}
public class TestDeadLock {
public static void main(String[] args) {
Makeup m1 = new Makeup();
m1.girl="大 丫";
m1.flag=0;
Makeup m2 = new Makeup();
m2.girl="小 丫";
m2.flag=1;
m1.start();
m2.start();
}
}