线程和进程
进程
- 进程是操作系统中运行的一个任务(一个应用程序运行在一个进程中)
- 进程(process)是一块包含了某些资源的内存区域。操作系统利用进程把它的工作划分为一些功能单元。
- 进程中所包含的一个或多个执行单元称为线程(thread)。进程还拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问。
- 线程只能归属于一个进程并且它只能访问该进程所拥有的资源。当操作系统创建一个进程后,该进程会自动申请一个名为主线程或首要线程的线程。
线程
- 一个线程是进程的一个顺序执行流
- 同类的多个线程共享一块内存空间和一组系统资源,线程本身有一个供程序执行时的堆栈。线程在切换时负荷小,因此,线程也被称为轻负荷进程。一个进程中可以包含多个线程。
进程与线程的区别
- 一个进程至少有一个线程。
- 线程的划分尺度小于进程,使得多线程程序的并发性高。另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大的提高了程序的运行效率。
- 线程在执行过程中与进程的区别在于每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看作多个独立的应用来实现进程的调度和管理以及资源分配。
线程使用的场合
- 线程通常用于在一个程序中需要同时完成多个任务的情况。我们可以将每个任务定义为一个线程,使他们的已一同工作。
- 也可以用于在单一线程中可以完成,但是使用多线程可以更快的情况,比如下载文件。
多线程
并发原理:
- 多个线程“同时”运行只是我们感官上的一种表现。事实上线程是并发运行的,os将时间划分为很多时间片段(时间片),尽可能均匀分配给每一个线程,获取时间片段的线程被cpu运行,而其他线程全部等待。所以微观上是走走停停的,宏观上都在运行。这种现象叫并发,但不是绝对意义上的“同时发生”。
- 多线程将代码执行的方式改变了,将原有的“串行操作”改为了“并行操作”
线程的状态图:
创建线程
线程有两种创建方式:
-
继承Thread从写run方法
- 线程的启动和调用
- 启动线程时要调用线程的start方法。
- 注意:不要直接调用run方法。
- 当start方法调用完毕后,该线程纳入到线程调度器中,一旦分配到时间片,那么它的run方法会自动被调用
- 这种创建线程的方式有两个设计不足:
1. 由于java是单继承的,这导致若继承了线程就无法再继承其它类去复用方法,这在实际开发中很不方便。
2. 直接从写run方法来定义线程任务会导致线程与任务存在一个必然的耦合关系,不利于线程的重用。public static void main(String[] args) { Thread1 t1 = new Thread1(); Thread2 t2 = new Thread2(); t1.start(); t2.start(); } class Thread1 extends Thread{ public void run() { for(int i=0;i<1000;i++) { System.out.println("hello"); } } } class Thread2 extends Thread{ public void run() { for(int i=0;i<1000;i++) { System.out.println("nihao"); } } }
-
第二种创建线程的方式:实现Runable接口单独定义线程任务
public class ThreadDemo2 {
public static void main(String[] args) {
MyRunnable1 r1 = new MyRunnable1();
MyRunnable2 r2 = new MyRunnable2();
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
class MyRunnable1 implements Runnable {
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("hello");
}
}
}
class MyRunnable2 implements Runnable{
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("NIhao");
}
}
}
- 使用匿名内部类定义
public static void main(String[] args) {
Thread t3 = new Thread() {
public void run() {
for(int i=0;i<1;i++) {
System.out.println("开门啊!");
}
}
};
Thread t1 = new Thread(new Runnable() {
public void run() {
for(int i=0;i<1;i++) {
System.out.println("别躲在里面不出声");
}
}
});
Runnable r2 = new Runnable() {
public void run() {
for(int i=0;i<1;i++) {
System.out.println("我知道你在家!");
}
}
};
Thread t2 = new Thread(r2);
t1.start();
//Thread x = Thread.currentThread();
t2.start();
t3.start();
//System.out.println(x);
}
线程操作API
- 线程提供了一个静态方法:static Thread currentThread()
该方法可以获取运行这个方法的线程(它写在哪个方法里就是获取哪个方法的线程) - sleep阻塞:static void sleep(long ms)
- 该方法可以让运行这个方法的线程进入阻塞状态指定毫秒,当超时后线程会自动回到RUNNABLE状态等待再次并发运行
- sleep方法要求处理中断异常。当一个线程调用sleep方法处于阻塞状态的过程中,若此时该线程的中断方法被调用:interrupt(),那么此时该线程的睡眠阻塞会被立即打断,并在sleep这个方法处抛出中断异常。
注意:JDK8之前有一个要求(JDK8不再要求):当一个方法的局部内部类(定义在方法里的内部类)中引用了这个方法的其他局部变量时,这个变量必须声明为final的,这其实时由于JVM内存分配问题导致的。
- 线程的优先级:priority
- 线程的优先级有10个等级,分别用整数1-10表示。1为最低,10为最高,5为默认值
- 理论上优先级越高的线程获取CPU时间片的次数越多。