【并发编程】线程的基础知识篇
创始人
2024-04-06 10:56:02
0

1.进程与线程的区别

(1)什么是进程和线程

  • **进程:**是系统进行分配和资源管理的最小单位。
  • **线程:**进程的一个执行单元,是进程内调度的实体,CPU调度和分派的最小单位,是程序执行的最小单位。

(2)线程与进程的区别

  • 进程有自己独立的地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据来维护代码块、堆栈段和数据段。
  • 线程是共享程序中的数据,使用相同的地址空间,因此CPU切换一个线程的花费远比进程小。
  • 线程之间通信更方便,同一进程下线程共享全局变量、静态变量等数据,而进程之间通信以通信方式进行。

在这里插入图片描述

2.线程的状态相互转换

(1)线程的六种状态

  • **新线程(NEW):**新创建一个线程对象,但还没有调用start()方法。
  • **运行(RUNNABLE):**处于可运行状态的线程正在JVM中执行,但它可能正在等待来自操作系统中的其他资源,例如处理器。
  • **阻塞(BLOCKED):**线程阻塞于synchronized锁,等待获取synchronized锁的状态的线程。
  • **等待(WAITINT):**obj调用wait()、join()等方法表示该线程进入等待状态,等待其他线程操作。
  • **超时等待(TIME_WAITING):**obj.wait(时间参数),Thread.join(),该状态的线程不同于WAITING,假如没有唤醒操作,它可以在指定时间内自行到RUNNABLE状态。
  • **终止(TERMINATED):**表示该线程已经执行完毕,死亡状态。

(2)线程状态验证

  • RUNNABLE状态
public static void main(String[] args) {new Thread(()->{try {System.in.read();} catch (IOException e) {e.printStackTrace();}}).start();
}

在这里插入图片描述

  • BLOCKED和TIME_WAITING状态
public static void main(String[] args) {Object obj = new Object();new Thread(()->{![在这里插入图片描述](https://img-blog.csdnimg.cn/c22e0fb0dac14470ac4867c266bf6a7d.jpeg#pic_center)synchronized (obj){try {//当线程拿到锁,无限睡眠,不释放锁System.out.println("线程1拿到锁");Thread.sleep(100000000L);} catch (InterruptedException e) {e.printStackTrace();}}}).start();new Thread(()->{synchronized (obj){//线程2模拟拿不到锁System.out.println("线程2拿到锁");}}).start();}

在这里插入图片描述

在这里插入图片描述

  • WAITING状态
public static void main(String[] args) {Object obj = new Object();new Thread(()->{synchronized (obj){try {//获取锁对象调用wait()让其进入等待状态obj.wait();} catch (InterruptedException e) {e.printStackTrace();}}}).start();}

在这里插入图片描述

(3)线程的状态转换

在这里插入图片描述

3.创建线程的方式

(1)继承Thread类重写run()方法

public class MyThread extends Thread {@Overridepublic void run(){System.out.println("线程运行");}
}
public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start();
}

在这里插入图片描述

(2)实现Runnable接口重写run()方法

public class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println("线程运行");}
}
public static void main(String[] args) {new Thread(new MyRunnable()).start();
}

在这里插入图片描述

(3)实现Callable接口重写call()方法

public class MyCallable implements Callable {@Overridepublic Integer call() throws Exception {System.out.println("线程运行");return 1;}
}
public static void main(String[] args) {MyCallable callable = new MyCallable();FutureTask futureTask= new FutureTask<>(callable);new Thread(futureTask).start();
}

在这里插入图片描述

4.线程的挂起和恢复

(1)什么是线程的挂起

  • 线程的挂起操作实质上就是使线程进入“非可执行”的状态下,在这个状态下CPU不会分给线程时间片,进入这个状态可以用来暂停一个线程的运行。
  • 当线程挂起后,可以通过重新唤醒线程来使之回复运行。

(2)如何挂起线程

  • 被废弃的方法
    • thread.suspend()该方法不会释放线程所占的资源。如果使用该方法将某个线程挂起,则可能会使其他等待资源的线程死锁。
    • thread.resume()方法本身并无问题,但是不能独立于suspend()方法存在。
  • JDK新的挂起唤醒方法
    • wait()暂停执行、放弃已经获得的锁、进入等待状态。
    • notify()随机唤醒一个在等待的线程。
    • notifyAll()唤醒所有在等待锁的线程,自行抢占CPU资源。

(3)suspend()、resume()案例

public class SuspendDemo implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"执行run()方法,调用suspend()方法之前");/*** 线程挂起(废弃的方法)*/Thread.currentThread().suspend();System.out.println(Thread.currentThread().getName()+"执行run()方法,调用suspend()方法之后");}
}
public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new SuspendDemo());thread.start();/*** 对主线程进行休眠,以防目标线程先执行了唤醒操作,在执行挂起操作*/Thread.sleep(3000L);thread.resume();
}

在这里插入图片描述

  • 注意:suspend()挂起时,不会释放锁资源,会发生死锁,如果主线程不休眠很容易让线程先调用唤醒,从而让线程一直处在挂起的状态。

在这里插入图片描述

(4)wait()、notify()案例

public class WaitDemo implements Runnable{private static Object object = new Object();@Overridepublic void run() {synchronized (object){try {System.out.println(Thread.currentThread().getName()+"获取锁资源");object.wait();System.out.println(Thread.currentThread().getName()+"执行业务逻辑");System.out.println(Thread.currentThread().getName()+"释放锁资源");} catch (InterruptedException e) {e.printStackTrace();}}}
}
public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(new WaitDemo2(),"线程1");thread1.start();Thread.sleep(3000L);synchronized (object){object.notify();}
}

在这里插入图片描述

5.线程的中断操作

(1)stop()废弃方法,开发中不要使用,因为一旦调用,线程就会立刻停止,因此有可能会引发线程安全性问题。

public class InterruptedDemo extends Thread{private int i = 0;private int j = 0;@Overridepublic void run() {i++;try {Thread.sleep(2000L);} catch (InterruptedException e) {e.printStackTrace();}j++;}public void printf(){System.out.println("i:"+i);System.out.println("j:"+j);}
}
public static void main(String[] args) throws InterruptedException {InterruptedDemo thread1 = new InterruptedDemo();thread1.start();Thread.sleep(1000L);thread1.stop();thread1.printf();
}

在这里插入图片描述

在这里插入图片描述

(2)Thread.interrupt()方法,改变线程终止的标识,默认是false,线程执行,调用interrupt()方法标识改成true,线程停止执行。

public class InterruptedDemo extends Thread{@Overridepublic void run() {System.out.println("线程终止标识:"+Thread.currentThread().isInterrupted());while (!Thread.currentThread().isInterrupted()){System.out.println("业务逻辑执行");}System.out.println("线程终止标识:"+Thread.currentThread().isInterrupted());}
}
public static void main(String[] args) throws InterruptedException {InterruptedDemo thread1 = new InterruptedDemo();thread1.start();Thread.sleep(1);thread1.interrupt();
}

在这里插入图片描述
在这里插入图片描述

(3)自定义标识,通过判断来终止线程执行

public class InterruptedDemo extends Thread{//注意这里一定要加上volatile,防止指令重排private static volatile boolean FLAG = true;@Overridepublic void run() {System.out.println("线程执行标识:"+FLAG);while (FLAG){System.out.println("业务逻辑执行");}System.out.println("线程执行标识:"+FLAG);}
}
public static void main(String[] args) throws InterruptedException {InterruptedDemo thread1 = new InterruptedDemo();thread1.start();Thread.sleep(1);FLAG = false;
}

在这里插入图片描述
在这里插入图片描述

6.线程的优先级

  • 线程的优先级告诉程序该线程的重要程度有多大,如果有大量线程被堵塞,都在等待运行,程序会尽可能的先运行优先级大的那个线程。但是这并表示优先级低的线程不会先运行。若线程的优先级较低,只不过表示它被允许运行的机会小一些。
  • 线程的优先级设置可以为1-10的任意数值,1的优先级最小,10的优先级最大,Thread类中定义了三个线程的优先级,MIN_PRIORITY(1)、NORM_PRIORITY(5)、MAX_PRIORITY(10),一般情况下是这几个常量值,不建议自行设置其他值。
  • 不同平台,对线程的优先级的支持不同。不能过度依赖线程的优先级。
  • 线程优先级用处:需要快速处理的任务,设置高的优先级,不是很急的任务,设置低的优先级慢慢处理。
//setPrioity(num),设置最大优先级
thread.setPriority(Thread.MAX_PRIORITY);
  • 线程优先级测试demo
public class PriorityDemo implements Runnable{@Overridepublic void run() {while(true){System.out.println(Thread.currentThread().getName()+":运行");}}public static void main(String[] args) {Thread thread1 = new Thread(new PriorityDemo(),"线程1");Thread thread2 = new Thread(new PriorityDemo(),"线程2");//设置线程1的优先级最大thread1.setPriority(Thread.MAX_PRIORITY);//设置线程2的优先级最小thread2.setPriority(Thread.MIN_PRIORITY);thread1.start();thread2.start();}}

7.守护线程

  • 线程分类
    • 用户线程:用户自己new Thread的线程就是用户线程。
    • 守护线程:任何一个守护线程都是整个程序中所有用户线程的守护者,只要有活着的用户线程,守护线程就活着。当JVM实例中最后一个非守护线程结束时,也随JVM一起退出。
  • 守护线程的用处:jvm垃圾清理线程、主线程。
  • 建议: 尽量少使用守护线程,因其不可控不要在守护线程里去进行读写操作、执行计算逻辑
  • 守护线程demo
public class DaemonDemo implements Runnable {@Overridepublic void run() {while(true){System.out.println(Thread.currentThread().getName());}}public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(new DaemonDemo(),"线程1");//设置为守护线程thread1.setDaemon(true);thread1.start();Thread.sleep(2000L);}
}

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...