目录
3.3实现 Callable 接口,允许子线程返回结果、抛出异常
一、浅谈进程
进程:是操作系统资源分配的基本单位。它是程序的一次执行,是系统运行程序的基本单位。
一个进程包含多个线程,至少包含一个主线程。
问题1:那么什么是线程呢?
线程是比进程更小的执行单位(CPU最小的执行单位)
问题2:线程和与进程的区别(重点)
(1) 根本区别 :进程是操作系统分配内存资源的基本单位,线程是处理器任务调度和执行的基本单位
(2)资源开销: 每个进程都有独立的代码副本和数据空间,进程之间的切换,资源开销较大。线程可以看做是轻量级的进程,每个线程都有自己的独立的运行栈和程序计数器,线程之间的切换,资源开销较小。
(3)包含关系:一个进程包含多个线程的,在执行过程中,线程的执行不是线性串行的,它是并行同步进行的
(4)内存分配:一个进程中的多个线程共享本进程的内存空间和资源,进程之间的内存空间和资源相互独立
(5) 影响关系:一个进程崩溃后,在保护模式下,不会对其他线程造成影响,一个线程崩溃后,会对本进程造成影响,使其退出。故多线程比多线程健壮。
(6)执行过程:每个进程都有程序入口和程序出口,但是线程不能独立执行,必须依靠在应用程序(进程中),由应用程序提供多个线程执行控制。
二、JVM进程
进程启动后,jvm内存区域分配,各线程共享元空间区,堆区资源
每个线程都有自己的本地方法栈,程序计数器,虚拟机栈,线程是轻量级进程,资源开销小
JVM进程都包含哪些线程呢?
public static void main(String[] args) {
// 获取 Java 线程管理对象 ThreadMXBean
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 不需要获取的锁监视器 lockedMonitor 和 synchronizer 信息
// 仅获取线程和线程信息
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
// 遍历线程信息,仅打印线程 ID 和线程名称信息
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName());
}
}
执行结果
[5] Attach Listener 添加时间监听器
[4] Signal Dispatcher 分发处理给 JVM 信号的线程
[3] Finalizer 调用对象 finalize 方法的线程(重写object类)
[2] Reference Handler 清除 reference 线程
[1] main 主线程
那么有人会问了,如果是多线程呢,主线程和子线程都存在,会先执行哪一个呢?
答:main主线程与子线程同时运行,由操作系统调度,程序本身无法确定线程的调度顺序
三、线程的创建方式
3.1 通过继承Thread创建线程
//线程的子类
class Test extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class Demo03 {
public static void main(String[] args) {
Test t1 = new Test();
Test t2 = new Test();
Test t3 = new Test();
//启动线程
t1.start();
t2.start();
t3.start();
}
}
3.2 实现Runnable 接口(线程执行类)
class task implements Runnable {
@Override
public void run() {
for (char i = 'a'; i <= 'z'; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
//实现Runnable 接口(线程执行类)
public class Demo04 {
public static void main(String[] args) {
// 创建3个线程执行逻辑
task t2 = new task();
task t1 = new task();
task t3 = new task();
// 调用了一个普通的Java方法,当前线程并没有任何改变,也不会启动新线程。
// t1.run();
// t2.run();
// t3.run();
Thread thread = new Thread(t1);
Thread thread1 = new Thread(t2);
Thread thread2 = new Thread(t3);
thread.start();
thread1.start();
thread2.start();
// 主线程继续同时向下执行
for (int i = 0; i < 100; i++) {
System.out.println("主线程" + i + " ");
}
System.out.println("主线程执行完毕了");
}
}
3.3实现 Callable 接口,允许子线程返回结果、抛出异常
//Runable:不会产生运行结果
//Callable:会产生运行结果
class task01 implements Callable<Integer> {
private int begin;
private int end;
public task01(int begin, int end) {
this.begin = begin;
this.end = end;
}
@Override
public Integer call() throws Exception {
int total = 0;
for (int i = begin; i <= end; i++) {
total += i;
}
return total;
}
}
//方式三::实现 Callable 接口,允许子线程返回结果、抛出异常
public class Demo06 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
task01 t1 = new task01(0, 5);
task01 t2 = new task01(6, 10);
// 创建FunturnTask对象,封装Callable接口实现类对象(线程执行逻辑)
FutureTask<Integer> f1 = new FutureTask<Integer>(t1);
FutureTask<Integer> f2 = new FutureTask<Integer>(t2);
// 分别启动两个子线程
new Thread(f1).start();
new Thread(f2).start();
// 分别获取两个子线程的计算结果
int a1 = f1.get();
int b1 = f2.get();
System.out.println(a1 + b1);
}
}
3.4线程池
线程池,按照配置参数(核心线程数、最大线程数,存活时间,拒绝策略,工作队列)创建并管理若干线程对象。程序中如果需要使用线程,将一个执行任务传给线程池,线程池就会使用一个空闲状态的线程来执行这个任务。执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。使用线程池可以很好地提高性能。
public class Demo07 {
public static void main(String[] args) {
// 创建线程池 包含10个线程
// pool-1-thread-1:0 线程池编号-分配线程编号
ExecutorService executorService = Executors.newFixedThreadPool(10);
while (true) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("当前线程:" + Thread.currentThread().getName());
try {
// 让当前线程休眠2秒,继续执行当前线程
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
四、线程的命名
调用父类的setName()方法或在构造方法中给线程名字赋值;
如果没有为线程命名,系统会默认指定线程名,命名规则是Thread-N的形式
//为线程命名
public class Demo08 {
public static void main(String[] args) {
// 利用构造方法命名
new Thread(new Task04(), "线程1").start();
new Thread(new Task04(), "线程2").start();
Thread t2 = new Thread(new Task04());
// 利用setName("")
t2.setName("线程3");
t2.start();
}
}
class Task04 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}