时间:2022年8月1日
线程:是进程的一个实体,是 cpu 调度和分派的基本单位,是比进程更小的可以独立运行的基本单位。
进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作系统进行资源分配和调度的一个独立单位。
特点:线程的划分尺度小于进程,这使多线程程序拥有高并发性,进程在运行时各自内存单元相互独立,线程之间内存共享,这使多线程编程可以拥有更好的性能和用户体验。
并发是指一个时间段中有几个程序都处于已启动到完毕之间,但任一个时刻点上只有一个程序在运行。
并行是指多个程序是在同一时刻点上是一起执行的。
1.继承 Thread 类并重写 run 方法创建线程,实现简单但不可以继承其他类
package com;
public class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("MyThread1 = " + i);
}
}
}
package com;
public class MyThread2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("MyThread2 = " + i);
}
}
}
@Test
public void test1(){
MyThread1 myThread1 = new MyThread1();
MyThread2 myThread2 = new MyThread2();
myThread1.start();
myThread2.start();
}
2.实现 Runnable 接口并重写 run 方法。避免了单继承局限性,编程更加灵活,实 现解耦。
package com;
public class MyThread3 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("MyThread3 = " + i);
}
}
}
@Test
public void test2(){
Thread thread = new Thread(new MyThread3());
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println("test2 = " + i);
}
}
3…实现 Callable 接口并重写 call 方法,创建线程。可以获取线程执行结果的返回 值,并且可以抛出异常。
package com;
import java.util.concurrent.Callable;
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println("MyCallable = " + i);
}
return "s";
}
}
@Test
public void test3() throws Exception {
MyCallable callable = new MyCallable();
FutureTask<Object> future = new FutureTask<>(callable);
new Thread(future).start();
for (int i = 0; i < 100; i++) {
System.out.println("test3 = " + i);
}
}
4.使用线程池创建(使用 java.util.concurrent.Executor 接口)
package com;
public class MyThreadPool implements Runnable {
@Override
public void run() {
Thread thread = Thread.currentThread();
System.out.println("线程池:" + thread.getName() + "访问网站");
}
}
@Test
public void test4() throws Exception {
ExecutorService threadPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
threadPool.submit(new MyThreadPool());
}
}
}
主要区别:
Runnable 接口 run 方法无返回值;
Callable 接口 call 方法有返回值,支持泛型 Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息
线程对象调用 run 方法不开启线程。仅是对象调用方法。
线程对象调用 start 开启线程,并让 jvm 调用 run 方法在开启的线程中执行调用 start 方法可以启动线程,并且使得线程进入就绪状态,而 run 方法只是 thread 的一 个普通方法,还是在主线程中执行。
第一是 new->新建状态。在生成线程对象,并没有调用该对象的 start 方法,这是线程处于 创建状态。
第二是 Runnable->就绪状态。当调用了线程对象的 start 方法之后,该线程就进入了就绪 状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。
第三是 Running->运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线 程就进入了运行状态,开始运行 run 函数当中的代码。
第四是阻塞状态。阻塞状态是线程因为某种原因放弃 CPU 使用权,暂时停止运行。直到线程 进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(1)等待 – 通过调用线程的 wait() 方法,让线程等待某工作的完成。
(2)超时等待 – 通过调用线程的 sleep() 或 join()或发出了 I/O 请求时,线程会进入到阻塞状态。 当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状 态。
(3)同步阻塞 – 线程在获取 synchronized 同步锁失败(因为锁被其它线程所占用),它会进入同 步阻塞状态。