多线程有什么用
发挥多核CPU的优势
随着工业的进步,现在的笔记本、台式机乃至商用的应用服务器至少也都是双核的, 4 核、 8 核甚至 16 核
的也都不少见,如果是单线程的程序,那么在双核CPU上就浪费了50%,在 4 核CPU上就浪费了75%。单
核CPU上所谓的”多线程”那是假的多线程,同一时间处理器只会处理一段逻辑,只不过线程之间切换得
比较快,看着像多个线程”同时”运行罢了。多核CPU上的多线程才是真正的多线程,它能让你的多段逻
辑同时工作,多线程,可以真正发挥出多核CPU的优势来,达到充分利用CPU的目的。
防止阻塞
从程序运行效率的角度来看,单核CPU不但不会发挥出多线程的优势,反而会因为在单核CPU上运行多
线程导致线程上下文的切换,而降低程序整体的效率。但是单核CPU我们还是要应用多线程,就是为了
防止阻塞。试想,如果单核CPU使用单线程,那么只要这个线程阻塞了,比方说远程读取某个数据吧,
对端迟迟未返回又没有设置超时时间,那么你的整个程序在数据返回回来之前就停止运行了。多线程可
以防止这个问题,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的
执行。
便于建模
这是另外一个没有这么明显的优点了。假设有一个大的任务A,单线程编程,那么就要考虑很多,建立
整个程序模型比较麻烦。但是如果把这个大的任务A分解成几个小任务,任务B、任务C、任务D,分别
建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。
多线程和单线程的区别和联系?
- 在单核 CPU 中,将 CPU 分为很小的时间片,在每一时刻只能有一个线程在执行,是一种微观上轮流
占用 CPU 的机制。
- 多线程会存在线程上下文切换,会导致程序执行速度变慢,即采用一个拥有两个线程的进程执行所需
要的时间比一个线程的进程执行两次所需要的时间要多一些。
结论:即采用多线程不会提高程序的执行速度,反而会降低速度,但是对于用户来说,可以减少用户的
响应时间。
简述线程、程序、进程的基本概念。以及他们之间关系是什么?
线程
与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与
进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在
各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。
程序
是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。
进程
是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个
进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令
接着一个指令地执行着,同时,每个进程还占有某些系统资源如 CPU 时间,内存空间,文件,输入输出
设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程是进程划分成的更
小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程
中的线程极有可能会相互影响。从另一角度来说,进程属于操作系统的范畴,主要是同一段时间内,可
以同时执行一个以上的程序,而线程则是在同一程序内几乎同时执行一个以上的程序段。
线程的创建方式
继承Thread类,作为线程对象存在(继承Thread对象)
常规方法,不多做介绍了,interrupted方法,是来判断该线程是否被中断。(终止线程不允许用stop方
法,该方法不会施放占用的资源。所以我们在设计程序的时候,要按照中断线程的思维去设计,就像上
面的代码一样)。
public class CreatThreadDemo1 extends Thread{
/**
* 构造方法: 继承父类方法的Thread(String name);方法
* @param name
*/
public CreatThreadDemo1(String name ){
super(name);
}
@Override
public void run () {
while ( !interrupted()){
System.out .println(getName() +"线程执行了..." );
try {
Thread.sleep(200 );
} catch ( InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main (String[] args ) {
CreatThreadDemo1 d1 = new CreatThreadDemo1("first");
CreatThreadDemo1 d2 = new CreatThreadDemo1("second");
d1.start();
d2.start();
d1.interrupt(); // 中断第一个线程
}
}
让线程等待的方法
Thread.sleep(200); //线程休息2ms
Object.wait(); //让线程进入等待,直到调用Object的notify或者notifyAll时,线程停止休眠
实现runnable接口,作为线程任务存在
public class CreatThreadDemo2 implements Runnable {
@Override
public void run () {
while ( true ){
System.out .println("线程执行了..." );
}
}
public static void main (String[] args ) {
// 将线程任务传给线程对象
Thread thread = new Thread(new CreatThreadDemo2());
// 启动线程
thread.start();
}
}
Runnable 只是来修饰线程所执行的任务,它不是一个线程对象。想要启动Runnable对象,必须将它放
到一个线程对象里。
匿名内部类创建线程对象
创建带线程任务并且重写run方法的线程对象中,为什么只运行了Thread的run方法。我们看看Thread
类的源码,我们可以看到Thread实现了Runnable接口,而Runnable接口里有一个run方法。
所以,我们最终调用的重写的方法应该是Thread类的run方法。而不是Runnable接口的run方法。
public class CreatThreadDemo3 extends Thread{
public static void main (String[] args ) {
// 创建无参线程对象
new Thread(){
@Override
public void run () {
System.out .println("线程执行了..." );
}
}.start();
// 创建带线程任务的线程对象
new Thread(new Runnable() {
@Override
public void run () {
System.out .println("线程执行了..." );
}
}).start();
// 创建带线程任务并且重写run 方法的线程对象
new Thread(new Runnable() {
@Override
public void run () {
System.out .println("runnable run 线程执行了..." );
}
}){
@Override
public void run () {
System.out .println("override run 线程执行了..." );
}
}.start();
}
}
创建带返回值的线程
Callable接口介绍:
返回指定泛型的call方法。然后调用FutureTask对象的get方法得道call方法的返回值。
public class CreatThreadDemo4 implements Callable {
public static void main (String[] args ) throws ExecutionException,InterruptedException {
CreatThreadDemo4 demo4 = new CreatThreadDemo4();
FutureTask<Integer> task = new FutureTask<Integer>(demo4); //FutureTask最终实现的是runnable接口
Thread thread = new Thread(task );
thread.start();
System.out .println("我可以在这里做点别的业务逻辑... 因为 FutureTask是提前完成任务");
// 拿出线程执行的返回值
Integer result = task.get ();
System.out .println("线程中运算的结果为:" +result);
// 重写 Callable接口的 call方法
@Override
public Object call () throws Exception {
int result = 1;
System.out .println("业务逻辑计算中..." );
Thread.sleep(3000 );
return result;
}
}
}
定时器
线程池创建线程
利用Java8新特性stream实现并发
线程有哪些基本状态
Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态(
新建状态 NEW :
使用new关键字创建一个thread对象,刚刚创建出的这个线程就处于新建状态。在这个状态的线程没有与操作系真正的线程产生关联,仅仅是一个java对象。
可运行 RUNABLE:
正在进行运行的线程,只有处于可运行状态的线程才会得到cpu资源。
阻塞 BLOCKED :
在可运行阶段争抢锁失败的线程就会从可运行--->阻塞
等待 WAITING :
可运行状态争抢锁成功,但是资源不满足,主动放弃锁(调用wait()方法)。条件满足后再恢复可运行状态(调用notiy()方法)。
有时限等待 TIMED_WAITING:
类似于等待,不过区别在于有一个等待的时间,到达等待时间后或者调用notiy(),都能恢复为可运行状态。<br /> 有两种方式可以进入有时限等待:wait(Long)和sleep(Long)
终结 TERMINATED:
代码全部执行完毕后,会进入到终结状态,释放所有的资源。
如何停止一个正在运行的线程
- 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
- 使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的
方法。
如果只是调用run()方法,那么代码还是同步执行的,必须等待一个线程的run()方法里面的代码全部执行
完毕之后,另外一个线程才可以执行其run()方法里面的代码。
为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?
看看Thread的start方法说明哈~
JVM执行start方法,会另起一条线程执行thread的run方法,这才起到多线程的效果。
为什么我们不能直接调用run()方法?
如果直接调用Thread的run()方法,其方法还是运行在主线程中,没有起到多线程效果。