多线程
任务调度
大部分操作系统(Windows、Linux)的任务调度采用时间片轮转的抢占式调度方式,一个任务执行一段时间后强制暂停去执行下一个任务,每个任务轮流执行。执行的一段时间叫时间片,任务正在执行时的状态叫运行状态,任务执行一段时间被强制暂停去执行下一任务,被暂停的任务处于就绪状态等待下一个属于自己的时间片。这样每个任务都能执行,由于CPU执行效率高,时间片非常短,在各任务之间快速切换,给人一种感觉各任务是在同时进行的。
什么是进程和线程?
- 线程是程序执行流的最小执行单元,是实际运作单位。区别在于:进程是一个动态的过程,是活动的实体。简单说:一个应用程序的运行被看作一个进程,而线程是运行中实际的执行者,进程包含了很多个可以同时运行的线程。
- 进程:进程是程序的一次执行过程,是系统运行程序的基本单位。系统运行一个程序即是进程从创建、运行、消亡的过程。进程是系统进行资源分配和调度的一个独立单元。
- 线程:线程和进程相似,但线程是比进程更小的执行单位。一个进程在执行过程中可以产生多个线程。这些线程共享进程的堆和方法区,同时拥有各自的计数器、虚拟机栈、本地方法栈。线程是进程的一个实体是CUP调度和分配的基本单元。
- 总结:
- 线程是程序执行的最小单元,而进程是操作系统分配资源的最小单元
- 一个程序至少有一个进程,一个进程至少有一个线程。
- 线程的划分尺度小于进程,线程比进程更轻量级。
- 线程的开销小但是不利于资源的管理和保护,进程则相反。
- 从逻辑上讲 ,多线程的意义在于应用程序中,多个执行部分同时执行;但操作系统并没有将多线程看作多个独立的应用去实现资源分配和调度。
并行和并发:
- 并行是多个处理器或多核处理器同时处理多个不同的任务。
- 并发是一个处理器同时处理多个任务。
- 并发是一个处理器在同一时刻只能执行一条指令,但是各进程指令被快速的轮换执行,CPU高速切换让人觉得同时执行。
单线程和多线程的选择
单线程性能并不一定低,单线程没有锁和线程的切换,资源占用少。当切换和创建时间的原因小于代码执行时间时,多线程功能就能显现。
线程生命周期
初始化-运行(就绪-运行中)-阻塞-等待-超时等待-终止
Java线程变迁
- 线程创建后处于
NEW
(新建)状态,调用start方法后开始运行,线程此时处于ready
(可运行)状态,并不是已经运行,当可运行状态获取CPU的时间片,进入到Running状态。 - 当线程执行wait方法后,线程进入到waiting状态,此时需要其他线程调用notify或
notifyAll
方法,才能将线程返回到可运行状态。 timed_waiting
,相当于是给等待状态加一个时间期限,类似于sleep(long)或wait(long time ),可将线程至于超时等待状态。- 当线程调用同步方法时,在没有获取到锁时候会处于Blocked阻塞状态。
- 线程在执行
runnable
的run方法后,进入到terminated终止状态。 - 此外当调用
yield()
方法后只是谦让的允许当前线程让出 CPU,但具体让不让不一定由操作系统决定。如果让了那么当前线程则会处于Ready 状继续竞争 CPU直至执行。
为什么要是用多线程?
- 从计算机底层:线程可以比作轻量级的进程,是程序执行的最小单元,线程间调度和切换的成本远小于进程。多核时代,以为着多个线程可以同时进行,减小了线程上下文切换的开销。
- 互联网发展的趋势:现在的系统动则百万甚至千万的并发量,多线程编程正好是开发高并发系统的基础,利用好多线程可以大大提高系统整体的并发及性能。
线程方法解释:
- yield():让出CPU,但是不一定会让。
- wait() \ wait(long time):使线程进入等待状态。
- notify()、notifyAll():唤醒wait方法操作的线程。
- join():让持有当前同步锁的线程进入等待状态。其实一个synchronized方法。
wait和notify的使用:
注意:
1:哪个对象调用的wait,必须使用那个对象去调的notify。
2:wait和notify必须在synchronized中。
package com.weidd.best.multiThread;
public class ThreadStateTest {
public static void main(String[] args) {
final Object lock = new Object();
Thread thread = new Thread(new Runnable() {
public void run() {
synchronized (lock) {// wait必须在Synchronized中
try {
lock.wait();
} catch (InterruptedException e) {
}
}
}
});
thread.start();
System.out.println(thread.getName() + " state A:"
+ thread.getState());
try {
Thread.sleep(5000L);//睡眠5秒
synchronized (lock) {
lock.notify();//唤醒等待
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thread.getName() + " state B:"
+ thread.getState());
System.out.println(thread.getName() + " state C:"
+ thread.getState());
System.out.println(thread.getName() + " state D:"
+ thread.getState());
System.out.println(thread.getName() + " state E:"
+ thread.getState());
}
}
执行结果:
Thread-0 state A:WAITING
Thread-0 state B:RUNNABLE
Thread-0 state C:TERMINATED
Thread-0 state D:TERMINATED
Thread-0 state E:TERMINATED
线程创建方式
- 继承Thread方式
// 使用继承的方式创建线程
public class ExtendsThreadTest extends Thread {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "**" + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new ExtendsThreadTest().start();
}
}
}
- 实现Runnable接口方式
public class ImplementRunnableThreadTest implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " ** " + i);
}
}
public static void main(String[] args) {
ImplementRunnableThreadTest run = new ImplementRunnableThreadTest();
for (int i = 0; i < 5; i++) {
new Thread(run, "线程--" + i).start();
}
}
}
- 实现Callable接口(与上面两种方式不同:其带有返回值及抛异常)
public class CallableThreadTest implements Callable {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+" -- call");
return 2 * 4;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> future = new FutureTask<Integer>(new CallableThreadTest());
new Thread(future).start();
System.out.println(Thread.currentThread().getName()+" ** main");
Integer integer = future.get();
System.out.println(integer);
}
}
多线程和多核
- 同一时间点只有一个任务执行。但是在多核处理器时呢?难道不是多个同时运行吗?