本篇博客将根据现有知识对Java多线程做以小结,以下博客仅作为个人学习过程的小结,如能对各位博友有所帮助不胜荣幸。
本篇博客将简单介绍计算机线程与进程的概念,以及创建线程的方法,重点叙述了线程的生命周期及其状态,后期随学习深入还会补充修改。
进程即一段程序的执行过程,是计算机中的程序关于某数据集合上的一次运行活动,是系统分配资源的最小单位
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位,一个进程可以有很多线程,每条线程并行执行不同的任务
并发编程:是指在一台处理器上“同时”处理多个任务。并发是在同一实体上的多个事件。多个事件在同一时间间隔发生
并发充分利用多核CPU的计算能力,通过并发编程的形式可以将多核CPU的计算能力发挥到极致,大大的提高性能,并且方便业务的拆分,提高系统的并发能力和性能;面对复杂的业务模型,并发程序会比串行程序更适合业务需求
虽然并发编程的目的是为了提高程序的执行效率,但有时还会遇到各种问题如:内存泄漏、频繁上下文切换、死锁的问题从而导致效率降低
解决多线程安全的办法:
当前任务在执行完CPU的时间片后切换到另一个任务之前会先保存自己的状态,以便下次时间片轮转再次回到这个任务的时候,可以再次加载这个任务的状态。任务从保存到在加载的过程就是一次上下文切换
最本质的区别:用户线程结束,JVM退出,不管这个时候还有没有守护线程运行。守护线程不会影响JVM的推出
内核态:涉及到安全相关的指令,权限要求比较高,通过系统接口调用
用户态:开发给用户程序可以直接让某设备执行的操作,不存在安全隐患
BIO:同步阻塞IO,执行到某行代码时挂起等待,系统内核再执行IO操作 用户程序(用户态 ===> 内核态) 如硬盘读取IO数据
NIO:同步非阻塞IO
1.继承自Thread类
(1) 定义一个Thread类的子类,重写run()方法,将相关操作实现,run()方法就是线程要执行的业务逻辑方法;
(2) 创建自定义的线程子类对象
(3) 调用子类实例的start()方法启动线程
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"的run方法正在执行");
}
}
class Main{
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
System.out.println(Thread.currentThread().getName()+"的main方法正在执行");
}
}
2.实现Runnable接口
(1)定义一个接口实现Runnable接口,并重写run()方法
(2)创建MyRunnable的实例myRunnable,以myRunnable为参数创建Thread对象
(3)调用线程对象的start()方法
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"的run方法正在执行");
}
}
class Main{
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
Thread t2 = new Thread(myRunnable);
t1.start();
t2.start();
System.out.println(Thread.currentThread().getName()+"的main方法正在执行");
}
}
3.实现Callable接口
(1)创建实现Callable接口的类MyCallable
(2)以MyCallable类的实例为参数创建FutureTask对象
(3)将FutureTake对象作为参数创建Thread对象
(4)调用线程对象的start()方法
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"的call方法正在执行");
return 1;
}
}
class Main{
public static void main(String[] args) {
MyCallable myCallable = new MyCallable();
FutureTask<Integer> futureTask1 = new FutureTask<Integer>(new MyCallable());
FutureTask<Integer> futureTask2 = new FutureTask<Integer>(new MyCallable());
Thread t1 = new Thread(futureTask1);
Thread t2 = new Thread(futureTask2);
t1.start();
t2.start();
System.out.println(Thread.currentThread().getName()+"的main方法执行完成");
}
}
4.使用Executors工具类创建线程池
Executors提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口
主要有 newFixedThreadPoll、newCachedThreadPool、newSingleThreadExecutor、newScheduleThreadPool这四种线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class MRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"run()方法执行中");
}
}
class Main{
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
MyRunnable myRunnable = new MyRunnable();
for(int i = 0; i < 5; i++){
executorService.execute(myRunnable);
}
executorService.shutdown();
}
}
相同点:
不同点:
多线程工作的流程:new一个Thread类,该线程被创建出来——调用start()方法,该线程进入就绪态——当该线程分配到时间片后进入运行态——此时线程自动调用run()方法执行其中的内容,由此实现多线程工作
如果调用run()方法,会把run()方法当成是一个main线程下的普通方法区执行,并没有另开辟一个线程来运行,本质上还是单线程运行
Callable接口类似于Runnable接口,但Runnable接口的run()方法不会反悔结果,并且无法抛出返回结果的异常;而Callable接口的功能更强大些,call()方法有返回值,这个返回值可以被Future拿到,并且可以抛出异常
Future接口表示异步任务,是一个可能还没有完成的异步任务的结果,Callable用于产生结果,Future用于获取结果
Future表示一个异步运算的任务。FutureTask里面可以传入一个Callable的具体实习类**,可以对这个异步运算的任务的结果进行等待获取**、判断是否已经完成、取消任务等操作。只有当运行完成的时候结果才能取回,如果运算尚未完成,get()方法将会阻塞。
一个FutureTack对象可以对调用了Callable和Runnable的对象进行包装,由于FutureTack也是Runnable接口的实现类,所以FutureTask也可以放入线程池中。
阻塞态可细分为三种:
1、等待阻塞:当处在运行态的线程执行wait()方法时,JVM就会将其放入等待队列中,线程进入等待阻塞状态,此时该线程会释放锁,直到被其他线程唤醒,重新进入同步队列,直到竞争锁成功重新进入就绪态
2、同步阻塞:当线程在获取synchronized同步锁失败,JVM会将其放入同步队列中,等待下次竞争锁,直到竞争成功进入就绪态
3、其他阻塞:当线程内部调用sleep()、join()或者发出IO请求时,线程会进入阻塞状态,直到sleep()设定的时间已过,join()等待线程销毁或者IO处理完成,此时线程重新进入就绪态,此过程全程不会释放锁
java虚拟机采用抢占式调度模型,值让优先级高的线程先分配CPU时间片,对于优先级相同的线程,采用随机分配,处于运行态的线程会一直执行,直到执行结束或被终止
终止线程的几种情况:
两者都可暂停当前线程
interrupt()、interrupted()、isInterrupted()方法的区别
此时如果线程在阻塞状态:
那么就会抛出InterruptedException异常,并重置中断标志位
如果线程不在阻塞状态:
使用Thread.interrupted()判断当前中断标志位是否被设置,并重置中断标志位
使用Thread.currentThread.isInterrupted()判断当前中断标志位是否被设置,不重置中断标志位
以上便是对java多线程的知识点小结,随着后续学习的深入还会同步的对内容进行补充和修改,如能帮助到各位博友将不胜荣幸,敬请斧正