目录
并发(Concurrent):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行
他们需要去抢占资源,夺取cpu
并行(Parallel):在同一时间内,有多条指令在多个处理器上运行。
进程(Process):进程就是正在运行中的程序,每一进程都有属于自己的存储空间和系统资源进程之间的资源不可共享
线程(Thread):一个进程包含若干个线程,线程之间共享资源。
1、进程与线程的区别:
本质区别:进程是操作系统分配的基本单位,线程是cpu处理的基本单位。
包含关系:一个进程包含若干个线程,但至少有一个线程
共享区别:进程之间资源不可共享,是单独存在的,而线程是基于进程存在的所以同一个进程之间的线程资源可以共享。
健壮性:一个进程崩溃后,在保护模式下其他进程不会被影响,但是一个线程崩溃可能导致整个进程被操作系统杀掉,所以多进程要比多线程健壮。
2、并发和并行的区别:
(1)、发生节点:
并发,指的是多个事情,在同一时间段内同时发生了。
并行,指的是多个事情,在同一时间点上同时发生了。
(2)、资源区别
并发的多个任务之间是互相抢占资源的。
并行的多个任务之间是不互相抢占资源的、
只有在多CPU的情况中,才会发生并行。否则,看似同时发生的事情,其实都是并发执行的。
3、线程的生命周期
线程的生命周期通常包括创建、就绪、运行、阻塞和销毁几个阶段。
- 创建:线程被创建并分配了必要的系统资源。
- 就绪:线程已经准备好运行,但尚未被调度执行。
- 运行:线程正在执行其任务。
- 阻塞:线程因等待某些条件而暂停执行,比如等待I/O操作完成或者等待其他线程释放资源。
- 销毁:线程执行完任务或者出现异常而被终止,释放其占用的资源。
4、如何实现线程?
4.1、继承Tread类
(1)自定义一个类MyThread类,用来继承与Thread类
(2)在MyThread类中重写run()方法
public class MyThread extends Thread{
//第一种创建线程的方法,继承Thread
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"线程启动ing");
}
}
}
(3)在测试类中创建MyThread类的对象
(4)启动线程
public class MyThread1Test {
public static void main(String[] args) {
//new一个Thread子类对象调用start方法
MyThread myThread = new MyThread();
MyThread myThread1 = new MyThread();
//给线程起名字,不起也是有默认值的
myThread.setName("线程1");
myThread1.setName("线程2");
myThread.start();//其实是父类的方法
myThread1.start();
}
}
4.2、实现Runnable接口
(1)创建一个类去实现Runnable接口
public class MyThread implements Runnable{
//创建线程的第二种方法:实现Runnable接口
//获取谁创建了实现类的线程
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"线程启动ing");
}
}
}
(2)在测试类中创建线程对象,并启动线程
public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThread1 = new MyThread();
//还要创建一个线程将自己所创建的任务交给他,直接实例化
Thread thread = new Thread(myThread);
Thread thread1 = new Thread(myThread1);
//起名字
thread.setName("线程1");
thread1.setName("线程2");
//启动线程
thread.start();
thread1.start();
}
}
4.3、实现Callable接口
(1)创建一个类来实现Callable接口
public class Mycallable implements Callable {
@Override
public String call() throws Exception {
String name = "hello";
String word="word";
return name+word;
}
}
(2)在测试类创建线程对象并启动
public class Mycallable implements Callable {
@Override
public String call() throws Exception {
String name = "hello";
String word="word";
return name+word;
}
}
4.4、创建线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建一个具有固定线程数量的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任务给线程池
for (int i = 0; i < 10; i++) {
executor.execute(new MyTask(i));
}
// 关闭线程池
executor.shutdown();
}
}
class MyTask implements Runnable {
private int taskId;
public MyTask(int taskId) {
this.taskId = taskId;
}
@Override
public void run() {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
}
}
4.4.1、线程池的特点:
-
重用线程:线程池会在内部维护一组线程,这些线程可被重用,避免了线程的频繁创建和销毁,提高了效率。
-
管理线程数量:可以控制线程池中线程的数量,通过配置可以创建固定数量的线程池、可变大小的线程池或者单线程的线程池,以满足不同的需求。
-
任务队列:线程池通常会包含一个任务队列,用于存放等待执行的任务。当线程池中的线程空闲时,会从任务队列中取出任务进行执行。
-
线程复用:线程池中的线程可以被重复使用,避免了线程创建和销毁的开销,提高了性能。
-
线程管理:线程池可以管理线程的生命周期,包括线程的创建、执行、销毁等操作,简化了线程的管理和调度。
-
控制并发:通过控制线程池的大小和任务队列的大小,可以限制并发执行的任务数量,防止系统资源耗尽和任务堆积导致的性能问题。
4.4.2、execute和submit的区别
- execute和submit都属于线程池的方法,execute只能提交Runnable类型的任务
- submit既能提交Runnable类型任务也能提交Callable类型任务。
- execute()没有返回值
- submit有返回值,所以需要返回值的时候必须使用submit
5、继承Tread类和实现runnable接口的区别
因为java只允许单继承,所以继承了Tread类就不能继承其他的类了
但是接口可以多实现,所以实现runnable接口的可扩展性就增强了
6、实现runnable和callable的区别
1,都是执行多线程,但是方法名称不同 run() 和call()
2, 实现Runnable方法是没有返回值,Callable会new一个Future对象,用来管理返回值
3, 实现Runnable方法没有抛出异常 而Callable有异常处理,并且获取异常