进程
- 进程是执行中的程序,是动态的,程序是静态的
- 进程是操作系统分配资源的最小单位,有一个进程控制块(PCB)和唯一的进程标识符(PID)
- 进程之间相互独立,内存不共享
- 每个进程都是一个实体,每个进程都有属于自己的一块内存
- 由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全——>进程的创建、销毁、切换开销比较大
- 进程只能创建子进程,无法创建平级进程
线程
- 线程是CPU的基本调度单位,是比进程更小的能独立运行的基本单位.
- 线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
- 一个进程可能包含多个线程,一个进程下的多个线程可以共享进程的内存区域
- 不同进程之间的线程相互不可见
- 一个线程可以创建和撤销另一个线程
并发和并行
线程并发就是两个以上线程在同一时间段在运行,cpu资源一直在切换分配多个线程。但某一时间点,必然只有一个线程在运行(单核CPU情况下)
并发指的是两个或者多个在同一时刻运行,相互不影响,资源独立。(多核CPU)
守护线程和非守护线程
典型代表:
守护:垃圾回收器
非守护:main(JVM启动的时候调用)
在JVM启动时,实际上有多个线程,但至少由一个非守护线程mian
创建方式
- 继承Thread类,实现一个run方法,直接使用.start()方法进行启动
嫌麻烦可以直接这样:
3. 实现Runable接口,实现run方法,将实现接口的类作为参数传入Thread类中,之后使用Thread的run方法
调用start的时候,至少有两个线程出现,一个时调用你的线程,一个是被调用启动的线程
线程生命周期
new、runnable、running、block、terminated
Thread和Runable区别和联系
runable接口将可执行的逻辑单元和线程控制分离开来
以银行家叫号说明为例:
/**
* 叫号类
* */
public class Ticket extends Thread{
private static int MAX = 50;
private final String name;
/*
加static只会在类初始化的时候实例化一次,多个线程共享index变量,但并发量大的时候依然会发生跳号,重号和超过最大值
不加的话每次实例化一个Ticket都会初始化index,它们之间不互通
*/
private static int index = 1;
public Ticket(String name) {
this.name = name;
}
@Override
public void run() {
while(index<=MAX){
System.out.println(name +"call"+index++);
}
}
}
/**
* 银行类
* /
public class Bank {
public static void main(String[] args) {
//实例化三次继承了thread的类
Ticket t1 = new Ticket("1号柜台");
t1.start();
Ticket t2 = new Ticket("2号柜台");
t2.start();
Ticket t3 = new Ticket("3号柜台");
t3.start();
}
}
输出如下:使用static,基本实现共享index
实现runable
public class Ticket1 implements Runnable {
private static int MAX = 50;
private static int index = 1;
@Override
public void run() {
while(index<=MAX){
System.out.println( Thread.currentThread()+"call : "+index++);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
//实例化一次实现runable的类
final Ticket1 t11 = new Ticket1();//添加final,变量不能指向别的引用变量了就
Thread tt1 = new Thread(t11,"一号窗口");
Thread tt2 = new Thread(t11,"二号窗口");
Thread tt3 = new Thread(t11,"三号窗口");
tt1.start();
tt2.start();
tt3.start();
}
以上两种输出其实都会有重复甚至是52输出(我只截了部分),说明什么?说明线程不安全啊
runnable的优势:实现的方式没有类的单继承性的局限性; 实现的方式更适合来处理多个线程有共享数据的情况。
协程
- 轻量级线程
- 协程由用户自己调度
- 协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
进程和线程的区别
- 一个程序至少有一个进程,一个进程至少有一个线程
- 进程自己独有的内存单元,多个线程共享进程的内存区域
- 一个进程死掉对于其它进程没有影响,但是一个线程死掉(非正常退出或者死循环)可能会影响其它线程正常工作(它不放手手里的资源),所以多线程程序没有多进程健壮。