Python 官方文档:入门教程 => 点击学习
目录1.你可以讲下进程与线程的区别?为什么要用多线程?2.什么是上下文切换?3.说说你知道的几种创建线程的方式4.昨天你讲到创建线程后使用start方法去调用线程,为什么ru
为什么要用多线程:
上下文切换一般发生在多线程情况下,因为一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。而在多核cpu下,多线程是并行工作的,如果线程数多,单个核又会并发的调度线程,运行时就会让一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于上下文切换。
对于我们Java程序线程来说,一旦一个线程抢占到CPU资源的使用权后,另一个线程需要保存当前的一个状态,以便下次抢占成功后可以回到当前状态,JVM中有块内存地址叫程序计数器,用于记录保存线程执行到哪一行代码,它是每个线程独有的。执行任务从保存到再次加载的过程就是上下文切换。
实际上,上下文切换也是对系统意味着来说会消耗大量的CPU时间,消耗大量资源。
以下几种情况会发生上下文切换。
创建线程有以下方式:
继承Thread类,重载它的run方法。
实现 Runnalbe接口,重载 Runnalbe接口中的run方法实现 。
实现Callable接口方式,重写Callable接口中的call方法,并且这个call方法可以有返回值。
需要注意三者的区别:
我们先来看看代码吧。
public class ThreadDemo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThead2 = new MyThread();
// myThread.start();
// myThead2.start();
myThread.run();
myThead2.run();
}
}
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 6; i++) {
System.out.println(Thread.currentThread().getName() + " :" + i);
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
这里我们创建了MyThread继承了Thread类,这种方法是一种可以创建线程的方式。接着我们在main方法中创建了两个线程,都调用了start方法和run方法。让我们先看看结果吧!
// 注释掉两个run方法 开启start方法得到的结果
Thread-0 :0
Thread-1 :0
Thread-1 :1
Thread-0 :1
Thread-1 :2
Thread-0 :2
Thread-1 :3
Thread-0 :3
Thread-1 :4
Thread-0 :4
Thread-1 :5
Thread-0 :5// 注释掉两个start方法 开启run方法得到的结果
main :0
main :1
main :2
main :3
main :4
main :5
main :0
main :1
main :2
main :3
main :4
main :5
接下来我们讲一下:
1.start方法的作用:
启动线程,相当于开启一个线程调用我们重写的run方法里面的逻辑,此时相当于有两个线程,一个main的主线程和开启的子线程。可以看到我们的代码,相当于有三个线程,一个主线程、一个Thread-0线程和一个Thread-1线程。并且线程之间是没有顺序的,他们是抢占cpu的资源来回切换的。
2.run方法的作用:
执行线程的运行时代码,相当于我们只是单纯的调用一个普通方法。然后通过主线程的顺序调用的方式,从myThread调用run方法结束后到myThread2去调用run方法结束,并且我们也可以看到我们控制台中的线程名字就是main主线程。
3.run方法我们可以重复调用,而start方法在一个线程中只能调用一次。即myThread这个实例对象只能调用一次start方法,如果再调用一次start方法的话,就会抛出IllegalThreadStateException
的异常。
4.我们调用start方法算是真正意义上的多线程,因为它是额外开启一个子线程去调用我们的run方法了。如果我们是调用run方法,就需要等待上一次的run方法执行完毕才能调用下一次。所以我们要调用start方法充分挥多核CPU的优势,采用多线程的方式去同时完成几件事情而不互相干扰。
我们可以通过查看Thread的源码中State枚举发现有6个状态:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
接下来我们具体来说说吧:
NEW(新建)
线程刚被创建,还只是一个实例对象,并未调用start方法启动。。MyThread myThread = new MyThread只有线程对象,没有线程特征。
Runnable(可运行)
在创建对象对象完成后,调用了myThread.start()方法线程,可以在Java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。也可以叫做处于就绪状态,需要等待被线程调度选中,获取cpu资源的使用权。
Teminated(被终止)
因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。代表着此线程的生命周期结束了。
处于运行状态中的线程由于某种原因,暂时放弃对 CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被 CPU 调用以进入到运行状态。有以下三种相关阻塞状态:
Blocked(锁阻塞)
当一个线程试图获取一个对象锁如(Synchronzied或Lock),而该对象锁被其他的线程持有,则该线程进入Blocked状态;只有当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待)
在调用了wait方法,JVM会把该线程放入等待队列中,等待另一个线程执行一个(唤醒),该线程此时状态表示进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
TimedWaiting(计时等待)
同waiting状态一样,调用sleep方法或者其他超时方法时,他们将进入Timed Waiting状态。不过这一状态只需保持到超时期满或者接收到唤醒通知。
sleep和wait方法他们都是可以暂停当前线程的执行,进入一个阻塞状态。
sleep:
我们可以指定睡眠时间,即让程序暂停指定时间运行,时间到了会继续执行代码,如果时间未到我们想要换醒需要调用interrupt
方法来随时唤醒即可。而调用interrupt
会使得sleep()方法抛出InterruptedException
异常,当sleep()方法抛出异常我们就中断了sleep的方法,从而让程序继续运行下去。
wait:
调用该方法,可以导致线程进入等待阻塞状态,会一直等待直到它被其他线程通过notify或者notifyAll方法唤醒。或者也可以使用wait(long timeout)
表示时间到了自动执行,类似于sleep(long millis)。
notify():
该方法会随机选择一个在该对象上调用wait方法的线程,解除其阻塞状态。
notifyAll():
该方法会唤醒所有的wait对象。
两者的区别:
这篇文章就到这里了,如果这篇文章对你也有所帮助,希望您能多多关注编程网的更多内容!
--结束END--
本文标题: 每日六道java新手入门面试题,通往自由的道路--多线程
本文链接: https://www.lsjlt.com/news/129557.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-03-01
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0