dede做招聘网站,工厂弄个网站做外贸如何处理,世界十大著名服装设计师,友情链接多线程API
进程和线程 进程#xff1a;进程就像是一个程序在电脑里运行时的一个实例。你可以把它想象成一个独立的小工人#xff0c;专门负责完成某项任务#xff08;比如打开浏览器、播放音乐#xff09;。每个进程都有自己独立的资源#xff08;比如内存#xff09;和…多线程API
进程和线程 进程进程就像是一个程序在电脑里运行时的一个实例。你可以把它想象成一个独立的小工人专门负责完成某项任务比如打开浏览器、播放音乐。每个进程都有自己独立的资源比如内存和工作空间不会轻易打扰其他进程。多个进程可以同时运行各自处理自己的任务。 线程线程就像是一个进程中的小工人。一个进程可以有多个线程每个线程负责进程中的某个具体工作比如在一个浏览器里同时加载不同的网页。线程之间可以共享进程的资源比如内存所以它们可以更快地协同工作。但因为它们共享资源也需要小心不要互相干扰。
进程和线程的关系 进程和线程的关系就像是公司和员工的关系。一个进程是公司它可以有多个员工线程一起工作。每个员工都有自己的任务但他们共享公司的资源比如办公设备。 进程切换是指CPU从一个公司进程跳到另一个公司执行不同公司的任务。因为每个公司有自己的资源所以这个切换需要花费时间去保存和加载这些资源。 线程切换是指CPU在同一个公司内换员工执行任务。因为员工共享公司的资源切换时不需要太多额外操作所以速度更快。 - CPU通过快速切换进程和线程来处理多任务确保每个任务都能得到执行。
进程与线程的区别 资源占用 进程进程是独立的拥有自己完整的资源集包括内存、文件句柄等。每个进程有自己独立的地址空间这使得进程之间相对隔离互不干扰。 线程线程是进程内的一个执行单元多个线程共享同一个进程的资源如内存、文件句柄。因此线程之间的资源共享更直接、效率更高但也更容易引发资源争用的问题。 开销 进程进程的创建、销毁和切换都需要较大的开销。因为进程切换涉及到保存和恢复独立的资源如内存状态、CPU寄存器等所以会消耗更多的系统资源和时间。 线程线程的创建、销毁和切换开销较小。因为线程共享进程的资源切换时只需保存和恢复少量的线程上下文所以切换速度更快系统资源占用更少。 并发性 进程进程之间的并发性较弱因为它们彼此独立不容易直接共享数据。要实现进程间的通信IPC通常需要借助系统提供的机制如管道、消息队列等这会增加复杂性和开销。 线程线程之间的并发性较强。由于线程共享进程的资源多个线程可以更容易地协同工作、直接共享数据从而提高并发执行的效率。但同时这也容易引发线程间的同步问题如数据竞争和死锁。
线程的生命周期 新建New线程对象被创建但还没有开始执行。就像一个人刚出生刚有了生命但还没有真正开始行动。 就绪Runnable线程已经准备好执行等待CPU调度。就像人长大到可以学习和工作的年龄准备好去做事等待机会开始行动。 运行Running线程获得CPU时间片开始执行任务。就像当人获得工作机会或任务开始真正行动比如开始学习、工作。 阻塞Blocked线程因等待某些资源如锁、IO而暂停执行直到资源可用。就像人遇到困难或等待某些条件比如等材料、等别人回应暂时不能继续工作只能等待问题解决。 终止Terminated线程执行完毕或因异常退出生命周期结束。就像人完成任务或退休生命结束所有活动停止。
线程优先级 线程优先级是一个数值用来决定线程获取CPU时间的先后顺序。优先级越高线程越有可能先被执行。 线程切换方式操作系统根据线程优先级和调度算法在多个线程间切换分配CPU资源。 设置线程优先级通过setPriority(int newPriority)方法设置优先级范围从1最低到10最高。 默认优先级所有线程默认优先级为5即中等优先级。 优先级只是一个建议具体调度由操作系统决定。 Java 中有三个线程优先级常量用于方便地设置线程的优先级 Thread.MIN_PRIORITY最小优先级值为1。适用于不紧急的任务。 Thread.NORM_PRIORITY普通优先级值为5。是默认优先级适用于一般任务。 Thread.MAX_PRIORITY最大优先级值为10。适用于需要优先处理的任务。 package day19;/*** 线程的优先级* 线程有10个优先级分别用整数1-10表示其中1是最低的10是最高的5是默认值*/public class PriorityDemo {public static void main(String[] args) {Thread thread01 new Thread(){Overridepublic void run() {for (int i 0; i 1000; i) {System.out.println(Min i);}}};Thread thread02 new Thread(){Overridepublic void run() {for (int i 0; i 1000; i) {System.out.println(Normal: i);}}};Thread thread03 new Thread(){Overridepublic void run() {for (int i 0; i 1000; i) {System.out.println(Max: i);}}};thread01.setPriority(Thread.MIN_PRIORITY);thread02.setPriority(Thread.NORM_PRIORITY);thread03.setPriority(Thread.MAX_PRIORITY);thread01.start();thread02.start();thread03.start();}}
sleep阻塞
sleep阻塞是指线程调用Thread.sleep(milliseconds)方法后进入阻塞状态暂停执行指定的时间。 作用sleep方法用于让当前线程暂停执行释放CPU让其他线程有机会运行。 特点线程在sleep期间保持着它的资源如锁只是暂时停止运行。当时间到达后线程自动恢复到就绪状态等待CPU再次调度。 注意sleep不会释放锁资源也不会影响线程的优先级。它只是简单地让线程暂停一段时间。 sleep阻塞常用于控制线程执行的节奏比如定时任务或轮询操作。 package day19;/*** Thread提供了一个方法static void sleep(long ms)* 暂停当前线程让出CPU让出时间片等待ms毫秒* 超时后会自动回到Runnable状态再次开始并发*/public class SleepDemo {public static void main(String[] args) {System.out.println(程序开始...);try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(程序结束了...);}} 在实际使用情况下sleep()有以下几种常见的用途 控制任务执行频率使用 sleep() 来暂停线程使其每隔一定时间执行任务。例如定时检查某个条件或定期更新状态。 模拟延迟在开发和测试过程中使用 sleep() 模拟网络延迟或处理时间以测试系统对延迟的反应。 避免资源过度占用在高频率的任务如轮询中使用 sleep() 减少对CPU的过度占用降低系统负载。 实现简单的等待机制在多线程程序中线程可能需要等待某些条件满足后才能继续执行。使用 sleep() 可以实现简单的等待或延时。 调试和测试在调试和测试中sleep() 可用于创建特定的时间间隔以便观察程序的行为或结果。 我们来写一个定时器Demo需求程序运行后在控制台上输入一个数字然后从该数字开始每秒递减到0时输出时见到。 package day19;import java.util.Scanner;public class ClockDemo {public static void main(String[] args) {System.out.println(请输入一个数字:);Scanner scanner new Scanner(System.in);System.out.println(倒计时开始);for (int i scanner.nextInt(); i 0; i--) {System.out.println(i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(时间到);}}
InterruptedExcption阻塞中断异常 InterruptedException 是 Java 中的一个异常表示线程在阻塞状态如 sleep()、wait() 或 join()时被中断了。 在 sleep() 方法中的作用 当线程调用 sleep() 方法进入阻塞状态时如果在等待期间有其他线程调用 interrupt() 方法中断这个线程sleep() 方法会抛出 InterruptedException。 这允许线程在被中断时可以立即恢复执行并处理中断请求。 使用场景 响应中断请求在长时间运行的线程中使用 sleep() 时可能会有其他线程请求中断。此时InterruptedException 让线程能够响应中断请求做出适当的处理如清理资源、退出循环。 中断支持的任务在需要支持中断的任务中比如一个线程正在等待或处理任务但也需要能够中断以快速终止任务或恢复其他操作使用 sleep() 时应考虑 InterruptedException。 实际开发中处理 InterruptedException 是重要的特别是在多线程程序中确保线程能够响应中断请求并适当地退出或清理资源。 示例 package day19;/*** 线程阻塞打断异常*/public class InterruptedExceptionDemo {public static void main(String[] args) {Thread thread new Thread(美女){Overridepublic void run() {try {System.out.println(getName() 刚美完容睡一觉吧...zZ);Thread.sleep(100000000);} catch (InterruptedException e) {System.out.println(getName() 干嘛呢干嘛呢干嘛呢);}}};Thread thread1 new Thread(装修工) {Overridepublic void run() {System.out.println(getName() 大锤80小锤40大哥你要几锤);for (int i 0; i 5; i) {System.out.println(getName() 80);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(咣当);System.out.println(getName() 大哥搞定);thread.interrupt();}};thread.start();thread1.start();}}
守护线程 什么是守护线程 守护线程Daemon Thread是指在后台运行的线程它为其他线程提供服务但不会阻止程序退出。守护线程通常用于执行不重要的任务如垃圾回收、后台监控等。 守护线程和普通线程的区别 生命周期 守护线程当所有用户线程非守护线程全部结束时守护线程才会结束。它不会阻止程序退出。 普通线程程序的退出会等到所有普通线程都结束后程序才会终止。 优先级 守护线程通常优先级较低因为它的任务是辅助性质的。 普通线程优先级可以根据任务的重要性设定。 如何使用守护线程 在创建线程时可以通过 Thread 类的 setDaemon(true) 方法将其设为守护线程。例如 void setDeamon(boolean) // 当参数为true时该线程为守护线程守护线程的特点 后台服务守护线程通常用于后台服务任务如垃圾回收、日志记录、监控等。 自动退出守护线程不会阻止程序终止当所有非守护线程结束时守护线程也会结束。 守护线程与垃圾回收GC 守护线程通常用于执行垃圾回收任务。Java 的垃圾回收线程就是一种守护线程它在后台运行清理不再使用的对象而不会阻止程序的终止。 守护线程在什么情况使用 后台任务适用于需要在后台持续运行的任务而这些任务不应该阻止程序退出。例如后台监控、定时任务、后台日志记录等。 辅助服务适用于提供服务或支持其他用户线程的任务比如线程池中的工作线程通常是守护线程。 package day19;public class DaemonThreadDemo {public static void main(String[] args) {// Rose线程Thread threadRose new Thread(Rose){Overridepublic void run() {for (int i 0; i 5; i) {System.out.println(getName() Let me go!!!);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(getName() AAAAAAAAAA!!!!);System.out.println(噗通~);}};// Jack线程Thread threadJack new Thread(Jack){Overridepublic void run() {while (true) {System.out.println(getName() You jump! I jump!);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};// 线程控制threadRose.start();threadJack.setDaemon(true); // 将threadJack设置为守护线程threadJack.start();}}
多线程
多线程并发安全 生活中的例子 共享银行账户 场景假设有一个银行账户两个不同的客户同时使用自动取款机ATM和柜台取款同一账户每个客户就是一个线程如果CPU执行客户1取款线程的一半时间片结束将线程分配给客户2检查余额时恰巧同时进入检查余额是否足够的阶段会不会出现都返回true的情况发生两个人都成功取款我们看下面的代码 package day19;/*** 当多个线程并发操作同一临界资源由于线程切换实际不确定导致执行顺序出现混乱而产生不良后果这就是线程并发安全问题** 临界资源操作该资源的过程同时只能被单个线程进行*/public class SyncDemo01 {static boolean success1 false; // 表示第一个人是否取款成功static boolean success2 false; // 表示第二个人是否取款成功public static void main(String[] args) {int sum 0; // 记录测试的次数Bank bank new Bank();while (true) {sum;Thread thread1 new Thread() {Overridepublic void run() {success1 bank.getMoney(20000); // 取款}};Thread thread2 new Thread() {Overridepublic void run() {success2 bank.getMoney(20000); // 取款}};thread1.start();thread2.start();try {Thread.sleep(10); // 阻塞主线程一段时间用于让t1t2线程执行完毕if (success1 success2) {System.out.println(成功两个人都取出20000块);break;} else {System.out.println(失败正常取款只有一人取款成功);success1 false;success2 false;bank.saveAccount(20000);}} catch (InterruptedException e) {e.printStackTrace();}System.out.println(第 sum 次测试:);}}}class Bank {private int account 20000; // 银行余额// 取钱方法返回true说明允许出钞返回false说明不允许出钞public boolean getMoney(int money) {int account getAccount(); // 查询余额方法if (account money) { // 判断余额是否足够account account - money; // 扣款Thread.yield(); // 主动放弃当前线程的时间片模拟到这里CPU恰好分配的时间片结束saveAccount(account); // 保存新余额return true; // 允许出钞}return false; // 不允许出钞}public void saveAccount(int account){this.account account;}public int getAccount() {return account;}} 并发安全为了防止两个客户取款同时修改账户余额导致不一致银行系统需要使用机制如锁来确保每次只有一个客户能够修改账户余额从而保证账户余额的准确性。 计算机相关的例子 线程安全的计数器 场景多个线程同时增加一个全局计数器的值。如果没有适当的同步机制多个线程同时对计数器进行操作可能会导致计数器值的丢失或重复增加。 并发安全为了确保计数器的正确性通常会使用线程安全的数据结构如 AtomicInteger或在增加计数器时使用同步机制如 synchronized 关键字或 Lock来保证每次操作的原子性从而避免数据竞争。
如何解决并发问题 同步Synchronization 同步在多线程编程中是指协调多个线程对共享资源的访问以防止并发操作导致数据的不一致性。由于多个线程可能同时访问和修改共享数据如果没有同步机制就会导致数据竞争和不可预测的结果。 synchronized 关键字 Java 提供了 synchronized 关键字来实现同步确保同一时刻只有一个线程可以执行被 synchronized 修饰的代码部分从而保护共享资源。 synchronized 的两种用法 同步方法 当一个方法被 synchronized 修饰时线程在调用这个方法之前需要获得方法所属对象的锁如果是静态方法则是类对象的锁。 当一个线程持有对象锁时其他线程必须等待直到这个锁被释放后才能访问这个同步方法。 public synchronized void exampleMethod() {// 同步代码
}同步代码块 通过 synchronized 关键字可以对一个特定的对象加锁通常是 this以确保某一代码块在同一时间只能被一个线程执行。 这种方式比同步整个方法更灵活只锁住关键的代码部分可能减少不必要的性能损失。 public void exampleMethod() {synchronized(this) {// 同步代码块}}优化刚才的代码 package day19;/*** 当多个线程并发操作同一临界资源由于线程切换实际不确定导致执行顺序出现混乱而产生不良后果这就是线程并发安全问题** 临界资源操作该资源的过程同时只能被单个线程进行*/public class SyncDemo01 {static boolean success1 false; // 表示第一个人是否取款成功static boolean success2 false; // 表示第二个人是否取款成功public static void main(String[] args) {int sum 0; // 记录测试的次数Bank bank new Bank();while (true) {sum;Thread thread1 new Thread() {Overridepublic void run() {success1 bank.getMoney(20000); // 取款}};Thread thread2 new Thread() {Overridepublic void run() {success2 bank.getMoney(20000); // 取款}};thread1.start();thread2.start();try {Thread.sleep(10); // 阻塞主线程一段时间用于让t1t2线程执行完毕if (success1 success2) {System.out.println(成功两个人都取出20000块);break;} else {System.out.println(失败正常取款只有一人取款成功);success1 false;success2 false;bank.saveAccount(20000);}} catch (InterruptedException e) {e.printStackTrace();}System.out.println(第 sum 次测试:);}}}class Bank {private int account 20000; // 银行余额// 取钱方法返回true说明允许出钞返回false说明不允许出钞public synchronized boolean getMoney(int money) {int account getAccount(); // 查询余额方法if (account money) { // 判断余额是否足够account account - money; // 扣款Thread.yield(); // 主动放弃当前线程的时间片模拟到这里CPU恰好分配的时间片结束saveAccount(account); // 保存新余额return true; // 允许出钞}return false; // 不允许出钞}public void saveAccount(int account){this.account account;}public int getAccount() {return account;}} 这段代码还存在性能问题原因是把 synchronized 锁放在了整个 getMoney() 方法上。虽然这样可以解决线程并发安全问题但会导致性能下降因为它限制了同一时间只有一个线程能够访问整个方法这在多线程环境中会降低系统的吞吐量。 生活中的类比去商场买衣服想象你去商场买衣服的过程 全程锁定如果商场为了避免拥挤每次只允许一个顾客进入并从头到尾完成所有购物步骤挑选、试穿、付款等这样可以确保秩序但效率非常低其他顾客必须等待很长时间才能轮到自己。 部分锁定更合理的做法是只在关键步骤上比如付款时进行控制同一时间只允许一个顾客在收银台结账而其他顾客可以同时进行挑选和试穿。这样可以大大提高整体效率。 如何解决锁带来的性能问题呢
同步块 同步块的组成和语法 同步块由 synchronized 关键字和一个同步监视器对象锁组成确保在同一时间只有一个线程可以执行同步块内的代码。 语法 java复制代码synchronized(锁对象) {// 需要同步的代码}同步监视器对象的选取 锁对象是同步块的关键它用于控制访问权限。 常见选择 this当前对象实例用于实例方法的同步。 类对象ClassName.class用于静态方法的同步。 任意其他对象用于细粒度控制避免锁定过多代码。 package day19;public class SyncDemo02 {public static void main(String[] args) {shop shop new shop();Thread thread01 new Thread(张禹垚一号) {Overridepublic void run() {shop.buy();}};Thread thread02 new Thread(张禹垚二号) {Overridepublic void run() {shop.buy();}};thread01.start();thread02.start();}}class shop {public void buy(){try {Thread thread Thread.currentThread(); // 获取当前线程对象System.out.println(thread.getName() 正在购买商品...);Thread.sleep(5000);synchronized (this) {System.out.println(thread.getName() 正在试衣服...);Thread.sleep(5000);}System.out.println(thread.getName() 购买成功);} catch (InterruptedException e) {e.printStackTrace();}}}选择合适的锁 锁粒度锁的范围应尽量小以减少线程争用。选择只需要保护共享资源的关键部分作为锁对象。 实例锁用 this 作为锁对象适用于保护当前实例的共享资源确保同一实例的同步方法或代码块同时只能被一个线程访问。 类锁用 ClassName.class 作为锁对象适用于保护静态变量或方法确保同类的所有实例共享同一把锁。 自定义锁用特定的对象作为锁适用于需要更灵活的同步控制避免过多的竞争。选择与共享资源最相关的对象。 一致性确保所有相关代码段使用同一个锁对象防止线程间产生不一致的锁定。