进程包含了指令、数据和执行状态等元素,是操作系统进行资源分配和调度的基本单位。进程简单来说就是表示正在运行的应用程序。我们在电脑上打开任务管理器也可以看到进程的基本内容,包括所占内存等。
而线程是指程序执行流的最小单元,是进程中的实际运作单位。线程是 CPU 调度的基本单位,也是多任务处理的基本单位之一。一个进程可以包含多个线程,不同的线程可以并发执行,共享进程的资源。
进程、线程和操作系统的关系其实就像是大学食堂、窗口和大学的关系。
大学中可以有许多个不同的食堂,它们之间互相独立,没有什么直接关联,就像操作系统中可以有不同的多个进程,它们也是相互独立,互不影响的;而线程就像是食堂中打菜的窗口,多线程可以节省时间,也可以让食堂的打菜操作变得更加方便。
我们在代码中要使用线程的话,我们就必须定义一个类去继承Thread(线程),这个类必须重写run() 方法,这样就能实现多线程的应用。对于线程,线程中有一个重要的方法叫做 start() 方法,它能够重新开一个子栈,然后把 run() 方法压到这个子栈中,实现与main() 线程的多线程。这种多线程可以用于同步或异步的并发执行任务,如高铁的售票窗口等等。
如下列代码:
public class Ticket extends Thread{
private int num;
public Ticket(){}
public Ticket(int num){
this.num = num;
}
public int getNum(){
return this.num;
}
public void setNum(int num){
this.num = num;
}
public void sale(){
this.num--;
System.out.println("剩余的票数 --->" + this.num);
}
@Override
public void run() {
while(getNum()>0){
sale();
}
}
}
这是一个售票类,继承了Thread,也重写了 run(),再写一个 sale() 方法表示售票,打印出剩余票数。
public class TestTicket {
public static void main(String[] args) {
Ticket ticket = new Ticket(100);
ticket.start();
while(ticket.getNum()>0){
System.out.println("main中剩余 --->" + ticket.getNum());
ticket.setNum(ticket.getNum()-1);
}
}
}
这个测试类实现了ticket和main的多线程关系,我们定义了票的总数为100张,然后让 ticket和 main 两个窗口一起卖票,直到票卖到 0。我们可以看看部分结果(太长就不全部展示了)。
我们可以看到结果是main 与 ticket 交替执行,因此我们也可以确定 ticket 另外开了一个子栈和main栈一起执行,虽然代码不是我们想象中的顺序地卖票,但我们可以看到多线程的实现差不多就是这样。对于这种现象,主要是因为当main线程去拷贝票数数据时,再拿了一张票但没有卖出去,此时ticket线程再去读取票数数据时,其实就是main线程卖票之前的数据,当ticket线程卖掉一张后就会出现94的情况(图中的最后一个数据),因此不会出现顺序卖票的现象。如果想要实现顺序卖票,则必须使用锁(lock),它能够在main线程拷贝数据后把数据锁死,不让其它线程修改,当main线程实现完之后再把锁解了,这样就能做到有序的运行。