1.基础概念
CPU核心数和线程数的关系
核心数:线程数=1:1 ;使用了超线程技术后—> 1:2
CPU时间片轮转机制
又称RR调度,会导致上下文切换
什么是进程和线程
进程:程序运行资源分配的最小单位,进程内部有多个线程,会共享这个进程的资源
线程:CPU调度的最小单位,必须依赖进程而存在。(先有进程再有线程。CPU先将资源分配给进程,然后线程再使用资源)
澄清并行和并发
并行:同一时刻,可以同时处理事情的能力
并发:与单位时间相关,在单位时间内可以处理事情的能力
高并发编程的意义、好处和注意事项
好处:充分利用cpu的资源、加快用户响应的时间,程序模块化,异步化
问题:
线程共享资源,存在冲突;
容易导致死锁;
启用太多的线程,就有搞垮机器的可能
2.认识Java里的线程
2.1 创建线程的三种方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
Runnable和Callable的区别: Runnable接口中的run方法没有返回值,而Callable接口中的call方法有返回值
public class MyThread extends Thread {
//实现Runnable接口
private static class UseRun implements Runnable{
@Override
public void run() {
System.out.println("I'm runnable implements");
}
}
//实现Callable接口
private static class UseCall implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("I'm callable implements");
return "callable";
}
}
//测试
public static void main(String[] args) throws ExecutionException, InterruptedException {
UseRun useRun = new UseRun();
//实现Runnable接口的类 需要交给线程类Thread去执行
new Thread(userRun).start();
UseCall useCall = new UseCall();
//PS:实现Callable的类无法直接转化为Thread 这就要使用FutureTask类 该类继承了Runnable接口
//相当于将UseCall -> Runnable -> Thread
FutureTask<String> futureTask = new FutureTask<>(userCall);
//执行该线程
new Thread(futureTask).start();
//获取并且打印UserCall的返回值
System.out.println(futureTask.get());
}
}
2.2 如何让Java里的线程安全停止工作
①线程自然终止:②自然执行完或抛出未处理异常
2.2.1 已经过时的三种方法:stop(),resume(),suspend()
- stop() :过于强硬会导致线程不会正确释放资源,
- suspend() (挂起) :处于挂起状态,线程不会释放资源 当其他的线程需要资源时 会导致资源无法获取 这样就容易导致死锁。
2.2.2 现在推荐使用的三种方法
- interrupt: 调用一个线程的interrupt() 方法中断一个线程,并不是强行关闭这个线程,只是跟这个线程打个招呼,将线程的中断标志位置为true,线程是否中断,由线程本身决定。
- isInterrupted() 判定当前线程是否处于中断状态(即判断线程标志位是否为true)。
- static方法interrupted() 判定当前线程是否处于中断状态,同时中断标志位改为false。
方法里如果抛出InterruptedException,线程的中断标志位会被复位成false,如果确实是需要中断线程,要求我们自己在catch语句块里再次调用interrupt()。 - interrupted()方法是静态的,底层调用了isInterrupted()方法,并且会将中断标志位重置。
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
这三种方法只有Thread以及其子类才能够使用
2.2.3 代码演示
1.继承Thread类创建线程方式 安全中断线程的代码:
public class EndThread {
private static class UseThread extends Thread{
public UseThread(String name){
super(name);
}
//重写Thread中的run方法
@Override
public void run(){
String name = Thread.currentThread().getName();
//判断当前的线程是否被中断
while(! isInterrupted()){
//获取当前运行线程的名字
System.out.println("Thread:" + name + " is run");
}
System.out.println("interrupt flag is : " + isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
//创建线程的实例对象
Thread endThread = new UseThread("endThread");
endThread.start();
//令主线程休眠2秒 这样就不会立刻执行后面的interrupt()方法
Thread.sleep(2000);
endThread.interrupt();
}
}
UseThread线程会运行2s 直到2s后main方法执行endThread.interrupt(); 线程中断。
执行结果:
Thread:endThread is run
Thread:endThread is run
Thread:endThread is run
Thread:endThread is run
Thread:endThread is run
Thread:endThread is run
interrupt flag is : true
- 如果将run方法中的代码改为如下:
@Override
public void run(){
String name = Thread.currentThread().getName();
//判断当前的线程是否被中断
while(true){
//获取当前运行线程的名字
System.out.println("Thread:" + name + " is run");
System.out.println("interrupt flag is : " + isInterrupted());
}
}
执行结果为:
Thread:endThread is run
interrupt flag is : false
Thread:endThread is run
interrupt flag is : false
Thread:endThread is run
interrupt flag is : false
interrupt flag is : true
Thread:endThread is run
interrupt flag is : true
Thread:endThread is run
interrupt flag is : true
Thread:endThread is run
程序会永远执行下去。 可以看到2s之前中断标志一直为false,2s后运行到主方法的endThread.interrupt();后中断标志变为true 但程序仍然会一直运行,这恰恰说明了 java的线程是协作式的并不是抢占式的。即使发出了中断线程的信号,线程仍然执行,因为线程是否中断最终的决定权是在线程本身。
2.实现Runnable接口方式创建线程 安全中断线程的方法
其实该方法和上面的方法大同小异 只是不能直接使用interrupt()、isInterrupted()、interrupted()这三种方法
public class EndRunnable {
private static class UseRunnable implements Runnable{
@Override
public void run() {
String name = Thread.currentThread().getName();//❤
//判断当前线程是否被中断 使用Thread.currentThread().isInterrupted() 是因为只有Thread类才有这个方法
while (! Thread.currentThread().isInterrupted()){
//❤
//获取当前运行线程的名字
System.out