当前位置: 首页 > news >正文

教育品牌网站建设吉安百度seo

教育品牌网站建设,吉安百度seo,买的网站模板怎么上传,wordpress页面跳舞Java多线程 线程池 线程池原理 创建方式#xff1a;newFixedThreadPool (固定数目线程的线程池)、newCachedThreadPool(可缓存线程的线程池)、newSingleThreadExecutor(单线程的线程池)、newScheduledThreadPool(定时及周期执行的线程池)、new ThreadPoolExecutor() #x…Java多线程 线程池 线程池原理 创建方式newFixedThreadPool (固定数目线程的线程池)、newCachedThreadPool(可缓存线程的线程池)、newSingleThreadExecutor(单线程的线程池)、newScheduledThreadPool(定时及周期执行的线程池)、new ThreadPoolExecutor() 自定义的方式创建 ## 线程池七大参数 - corePoolSize核心线程数 线程池当中线程数最基本上的数量只有当工作任务队列满了才会有新的线程被创建出来此时线程数才会大于该值- maximumPoolSize最大线程数 线程池中允许的最大线程数当前任务队列满了并且小于该值的时候线程才会被创建否则交给拒绝策略- 最大线程的存活时间如果当前线程空闲且线程数量大于核心数则线程销毁的超时时间- unit 时间单位- 阻塞队列当核心线程满后后面来的任务都进入阻塞队列- 线程工厂用于生产线程- 任务拒绝策略阻塞队列满后拒绝任务有四种策略1抛异常2丢弃任务不抛异常3打回任务4尝试与最老的线程竞争## 线程池的好处 1⃣️降低资源消耗 2⃣️提高响应速度 3⃣️使线程便于管理线程提交后的执行过程 a.如果正在运行的线程数量小于 corePoolSize那么马上创建线程运行这个任务! b.如果正在运行的线程数量大于或等于 corePoolSize那么将这个任务放入队列。 c.如果这时候队列满了而且正在运行的线程数量小于 maximumPoolSize那么还是要创建线程运行这个任务; d.如果队列满了而且正在运行的线程数量大于或等于 maximumPoolSize那么线程池会抛出异常告诉调用者我不能再接受任务了 五种阻塞队列 ArrayBlockingQueue有界队列是一个用数组实现的有界阻塞队列按FIFO排序量。LinkedBlockingQueue可设置容量队列基于链表结构的阻塞队列按FIFO排序任务容量可以选择进行设置不设置的话将是一个无边界的阻塞队列最大长度为Integer.MAX_VALUE吞吐量通常要高于ArrayBlockingQuenenewFixedThreadPool线程池使用了这个队列DelayQueue延迟队列是一个任务定时周期的延迟执行的队列。根据指定的执行时间从小到大排序否则根据插入到队列的先后排序。newScheduledThreadPool线程池使用了这个队列。PriorityBlockingQueue优先级队列是具有优先级的无界阻塞队列SynchronousQueue同步队列一个不存储元素的阻塞队列每个插入操作必须等到另一个线程调用移除操作否则插入操作一直处于阻塞状态吞吐量通常要高于LinkedBlockingQuenenewCachedThreadPool线程池使用了这个队列。 线程创建方式 (1)继承 Tread 类拥有run方法和start方法(2)实现 Runnable 接口只有一个run方法(3)实现 Callable 接口拥有一个call方法带有返回值在Runnable中我们无法对run()方法抛出的异常进行任何处理因为其没有返回值但在Callable中自定义的call()方法可以抛出一个checked Exception并由其执行者Handler进行捕获并处理。 run和start方法的区别 1. 定义位置不同 run()方法是Thread类中的一个普通方法它是线程中实际运行的代码线程的代码逻辑主要就是在run()方法中实现的。 start()方法是Thread类中的一个启动方法它会启动一个新的线程并在新的线程中调用run()方法。2. 执行方式不同 直接调用run()方法会像普通方法一样在当前线程中顺序执行run()方法的内容这并不会启动一个新的线程。 调用start()方法会创建一个新的线程并在新的线程中并行执行run()方法的内容。3. 线程状态不同 当我们调用start()方法启动一个新线程时该线程会进入就绪状态等待JVM调度它和其他线程的执行顺序。而当我们直接调用run()方法时则会在当前线程中执行不会产生新的线程。线程池的五种状态 1. RUNNING线程池一旦被创建就处于 RUNNING 状态任务数为 0能够接收新任务对已排队的任务进行处理。 2. SHUTDOWN不接收新任务但能处理已排队的任务。调用线程池的 shutdown() 方法线程池由 RUNNING 转变为 SHUTDOWN 状态。 3. STOP不接收新任务不处理已排队的任务并且会中断正在处理的任务。调用线程池的 shutdownNow() 方法线程池由(RUNNING 或 SHUTDOWN ) 转变为 STOP 状态。 4. TIDYINGSHUTDOWN 状态下任务数为 0 其他所有任务已终止线程池会变为 TIDYING 状态会执行 terminated() 方法。线程池中的 terminated() 方法是空实现可以重写该方法进行相应的处理。线程池在 SHUTDOWN 状态任务队列为空且执行中任务为空线程池就会由 SHUTDOWN 转变为 TIDYING 状态。线程池在 STOP 状态线程池中执行中任务为空时就会由 STOP 转变为 TIDYING 状态。 5. TERMINATED线程池彻底终止。线程池在 TIDYING 状态执行完 terminated() 方法就会由 TIDYING 转变为 TERMINATED 状态。线程池execute和submit方法的区别 方法返回不同 execute(Runnable command) 接受一个Runnable类型的参数而 submit(Runnable task) 接受一个Runnable或者Callable类型的参数因此 submit() 方法可以返回一个结果如果任务在执行过程中抛出异常 execute()方法不会显示抛出异常而是将其捕获并记录而 submit()方法则会将异常包装在Future对象中返回。阻塞行为不同execute()方法一旦提交任务就立即返回无法阻塞而 submit()方法可以选择传递一个超时时间作为参数如果在指定时间内任务没有完成则取消任务并抛出异常。 import java.util.concurrent.*; public class ThreadPoolExample {public static void main(String[] args) {// 创建线程工厂ThreadFactory threadFactory Executors.defaultThreadFactory();// 创建拒绝策略RejectedExecutionHandler rejectedExecutionHandler new ThreadPoolExecutor.AbortPolicy();// 创建线程池设置参数int corePoolSize 5;int maxPoolSize 10;long keepAliveTime 60; // 线程空闲时间TimeUnit unit TimeUnit.SECONDS; // 时间单位int queueCapacity 100; // 任务队列大小ThreadPoolExecutor executor new ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,unit,new LinkedBlockingQueue(queueCapacity),threadFactory,rejectedExecutionHandler);// 提交任务给线程池for (int i 0; i 10; i) {final int taskId i; // 任务ID仅用于示例executor.execute(new Runnable() {public void run() {System.out.println(Task taskId is executing by Thread.currentThread().getName());// 执行任务的具体逻辑// ...}});}// 关闭线程池executor.shutdown();} }守护线程 守护线程daemon thread是在计算机程序中运行的一种特殊线程。它的主要特点是当所有非守护线程结束时守护线程会自动退出而不会等待任务的完成。 守护线程通常被用于执行一些后台任务如垃圾回收、日志记录等。它们在程序运行过程中默默地执行任务不会阻塞主线程或其他非守护线程的执行。 与普通线程不同守护线程的生命周期并不影响整个程序的生命周期。当所有非守护线程结束时守护线程会被强制退出无论它的任务是否完成。 需要注意的是守护线程不能用于执行一些重要的任务因为它们可能随时被强制退出。此外守护线程也无法捕获或处理异常。 thread1.setDaemon(true); //设置守护线程线程结束 结束线程有以下三种方法 1设置退出标志使线程正常退出。 2使用interrupt()方法中断线程。 3使用stop方法强行终止线程不推荐使用Thread.stop, 这种终止线程运行的方法已经被废弃使用它们是极端不安全的 线程优先级 线程分为1-10级其中10最高默认值为5优先级的高低不代表线程优先执行JVM不一定采纳需要看CPU的情况一般情况下优先级高的先执行。 多线程并发的3个特性 原子性 即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断要 么就都不执行使用synchronized或者Lock悲观锁、AtomicXXX原子操作可见性当多个线程访问同一个变量时一个线程修改了这个变量的值其他线程能够立即 看得到修改的值 volitale、synchronized有序性程序执行的顺序按照代码的先后顺序执行单线程时为了提高执行效率程序在编译或者运行的时候会对代码进行指令重排保证最终一致性指令重排并不会产生问题但是在多线程的情况下便可能产生不希望看到的结果。使用volatile修饰的内存不可以重排序对volatile修饰变量的读写访问都不可以换顺序 ThreadLocal关键字 主要用于线程本地化存储即只有本线程才能对该变量进行查看或者修改使用时需要注意在不使用该变量的时候一定要调用remove方法删除变量否则可能会造成内存泄露的问题ThreadLocal 是由 ThreadLocalMap 实现k 为弱引用在没有强引用的情况下经可达性分析算法发现当前对象不可达经一次 GC 之后 k 就为 null ## Thread、ThreadLocal、ThreadLocalMap的关系 Thread 与 ThreadLocalMap 是 has a 的关系。初始时Thread 中的 threadLocals 为空只有在当前线程中创建了 ThreadLocal 变量并且设置了变量值才会创建 ThreadLocalMap 实例。ThreadLocal.ThreadLocalMap threadLocals null; ThreadLocal 类中定义了静态内部类 ThreadLocalMap把它当作伪 Map 即可就是存储key-value 键值对 ThreadLocalMap 类中又定义了 Entry 静态内部类该类定义了一个 Entry 类型的数组 table。为什么要自定义一个 Entry 呢因为现有不满足要定制Entry 的 k 为 ThreaLocal 实例v 为变量值使用注意 volatile关键字 可见性使用volatile关键字会强制将修改的值立即写入主存 原理 线程写Volatile变量的过程 改变线程本地内存中Volatile变量副本的值将改变后的副本的值从本地内存刷新到主内存 线程读Volatile变量的过程 从主内存中读取Volatile变量的最新值到线程的本地内存中从本地内存中读取Volatile变量的副本 //线程1 boolean stop false; while(!stop){doSomething(); } //线程2 stop true;确保有序性利用volatile的变量保证线程的执行顺序 volatile boolean inited false; //线程1: context loadContext(); inited true; //线程2: while(!inited ){sleep() } doSomethingwithconfig(context);## 问题volatile 能够保证线程安全问题吗为什么 不能volatile 只能保证可见性和顺序性不能保证原子性。线程状态 Java线程具有以下几种基本状态 NEW新建线程刚刚被创建但尚未调用 start() 方法以开始其执行。此时线程已分配内存空间并已完成初始化但它还没有开始执行代码。RUNNABLE可运行线程已被调用 start() 方法正处于就绪状态它已经准备好运行。BLOCKED阻塞线程因等待某个锁、被其他线程调用 wait() 方法、等待 I/O 操作完成等原因而暂时无法继续执行。TIMED_WAITING计时等待与普通等待状态相似但在计时等待状态下如果等待时间超过设定的超时时间线程将自动转换为就绪状态。这通常发生在使用了 wait() 方法且设置了超时参数的情况下。(无限等待状态(植物人状态):线程调用了wait()方法并且没有传参数 → 自己醒不过来,只能调用notify()方法来唤醒无限等待状态)TERMINATED终止线程完成了所有预定的任务并且已经结束了。这种情况下线程不再存在于系统中不能被再次激活。(run()方法执行结束,或者调用了stop(),或者发生了异常,线程就死亡了.) 此外还有两种特殊状态 WAITING等待线程正在等待其他线程的通知以便它可以继续执行。这可能是因为等待 notifyAll() 或 join() 方法的执行结果。DEAD死亡线程的 run() 方法已经执行完毕或者被中断或异常退出导致线程进入死亡状态。在这种状态下线程不能再作为独立执行的线程对待。 /*顾客去买包子,包子可能没有要现做, 但是不知道要多久才能做好,因为不知道顾客要等待的时间,所以调用了wait()方法,此时顾客属于无限等待状态 包子做好了,老板通知顾客包子好了,notify()一下*/ public class WaitAndNotify {public static void main(String[] args) {//锁对象必须是唯一的final Object obj new Object();//创建一个顾客线程new Thread(){Overridepublic void run() {while (true){//老板和顾客线程要用同步代码块包裹,保证只能实行其中的一个synchronized (obj){System.out.println(顾客说要1个素馅的1个肉馅的包子);//无限等待try {obj.wait();} catch (InterruptedException e) {e.printStackTrace();}//唤醒之后的代码System.out.println(包子已经做好了,开吃);System.out.println(______________________________);}}}}.start();//创建一个老板线程new Thread(){Overridepublic void run() {while(true){//花了3S做包子try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}//老板和顾客线程要用同步代码块包裹,保证只能实行其中的一个synchronized (obj){System.out.println(老板花3S做好了包子);obj.notify();}}}}.start();} }notify和notifyAll的区别 notify()是随机唤醒一个线程,notifyAll()唤醒所有线程都是让线程变为就绪状态 sleep和wait的区别 语法和使用sleep 直接作用于 Thread 类不需要与 synchronized 关键字一起使用。 wait 则是 Object 类的方法通常需要配合 synchronized 使用以确保正确性。唤醒机制sleep 会自动在指定的时间后唤醒线程如果未设置超时时间则会无限期地等待下去。 wait 不一定需要传递超时时间参数。如果不传递任何参数表示永久休眠若传递超时时间则在超时后唤醒。锁释放sleep 不会释放任何锁资源。 wait 会释放所持锁资源以便其他线程能够访问同步控制块或方法。使用sleep 通常用于使整个应用程序暂停执行而不是特定的同步控制块。 wait 更适合于在特定同步控制块内部暂停线程以便其他线程有机会处理该控制块的资源。 JAVA的锁 CAS锁Compare and Swap CAS是一种无锁算法 乐观锁CAS有3个操作数内存值V旧的预期值A要修改的新值B。当且仅当预期值A和内存值V相同时将内存值V修改为B否则什么都不做。 首先每个线程都会先获取当前的值。接着走一个原子的CAS操作原子的意思就是这个CAS操作一定是自己完整执行完的不会被别人打断。然后CAS操作里会比较一下现在你的值是不是刚才我获取到的那个值。如果是说明没人改过这个值那你给我设置成累加1之后的一个值。同理如果有人在执行CAS的时候发现自己之前获取的值跟当前的值不一样会导致CAS失败失败之后进入一个无限循环再次获取值接着执行CAS操作。 缺点 1.可能cas 会一直失败然后自旋 2.如果一个值原来是A变成了B然后又变成了A那么在CAS检查的 时候会发现没有改变但是实质上它已经发生了改变这就是所谓的ABA问题。 对于ABA问题其解决方案是加上版本号即在每个变量都加上一个版本号每次 改变时加1即A — B — A变成1A — 2B — 3A。AQS同步机制AbstractQueuedSynchronizer AQS的核心思想是通过一个volatile修饰的int属性state代表同步状态例如0是无锁状态1是上锁状态。多线程竞争资源时通过CAS的方式来修改state例如从0修改为1修改成功的线程即为资源竞争成功的线程将其设为exclusiveOwnerThread也称【工作线程】资源竞争失败的线程会被放入一个FIFO的队列中并挂起休眠当exclusiveOwnerThread线程释放资源后会从队列中唤醒线程继续工作循环往复。 Synchronized 锁 在Java中synchronized是一种关键字用于实现线程同步。它可以用于方法或代码块用于保证同一时间只有一个线程可以执行被synchronized修饰的代码。 synchronized的锁机制有两种使用方式 同步方法可以在方法声明中使用synchronized关键字。当一个线程调用同步方法时会自动获取该方法所属对象的锁其他线程将被阻塞直到该线程释放锁为止。同步代码块使用synchronized关键字可以修饰一段代码块。当一个线程进入synchronized代码块时它会尝试获取锁如果锁已经被其他线程获取那么该线程将被阻塞直到锁被释放。 synchronized锁是基于对象的每个对象都有一个关联的锁。当多个线程同时访问某个对象的同步方法或同步代码块时它们会竞争该对象的锁。1.如果synchronized锁加在实例方法上则默认使用的是this锁2.如果synchronized锁加载静态方法上则默认使用的是 类名.class 锁Java反射技术中说到一个class文件只会在jvm中存在一份另外要注意避免过多地使用synchronized因为过多的同步操作可能会导致性能下降。在某些情况下可以考虑使用更灵活的并发工具如Lock和Condition接口对象头 自旋线程会一直循环检查该锁是否被释放直到获取到该锁为止。这个循环等待的过程被称为自旋 1偏向锁 只有一个线程争抢锁资源的时候.将线程拥有者标识为当前线程。引入了偏向锁目的是来尽可能减少无竞争情况下的同步操作开销。当一个线程访问同步块并获取对象的锁时会将锁的标记记录在线程的栈帧中并将对象头中的Thread ID设置为当前线程的ID。此后当这个线程再次请求相同对象的锁时虚拟机会使用已经记录的锁标记而不需要再次进入同步块。 偏向锁Biased Locking就是为了在无竞争的情况下减少同步操作的开销。它通过记录线程ID来避免对锁的加锁和解锁操作提高了单线程访问同步代码块时的性能。2轻量级锁自旋锁 一个或多个线程通过CAS去争抢锁,如果抢不到则一直自旋。虚拟机会将对象的Mark Word复制到线程的栈帧中作为锁记录并尝试使用CASCompare and Set操作尝试获取锁。如果CAS成功则表示线程获取了轻量级锁并继续执行同步块。如果CAS失败说明有竞争虚拟机会通过自旋spinning等待其他线程释放锁 轻量级锁是为了减少线程切换的开销。它使用CASCompare and Set操作来尝试获取锁如果成功则可以继续执行同步块无需线程切换如果失败则会进行自旋操作等待锁的释放。自旋操作避免了线程挂起和切换的开销提高了多线程竞争时的性能。 使用对象头中的一部分位来存储线程ID和锁标记不需要额外的内存存储锁的状态。相对于传统的重量级锁它能够节省内存消耗。3重量级锁 如果自旋等待不成功虚拟机会将轻量级锁升级为重量级锁。在这种状态下虚拟机会将线程阻塞并使用操作系统的互斥量来实现锁的释放和获取。 需要注意的是锁的升级是逐级升级的过程而不会存在降级。换句话说一旦锁升级到更高级别就不会回到低级别。 升级过程 1当只有一个线程去争抢锁的时候,会先使用偏向锁,就是给一个标识,说明现在这个锁被线程a占有.2后来又来了线程b,线程c,说凭什么你占有锁,需要公平的竞争,于是将标识去掉,也就是撤销偏向锁,升级为轻量级锁,三个线程通过CAS自旋进行锁的争抢(其实这个抢锁过程还是偏向于原来的持有偏向锁的线程).3现在线程a占有了锁,线程b,线程c一直在循环尝试获取锁,后来又来了十个线程,一直在自旋,那这样等着也是干耗费CPU资源,所以就将锁升级为重量级锁,向内核申请资源,直接将等待的线程进行阻塞. 优化方法 ## 1、适应性自旋 解决这个问题最简单的办法就是指定自旋的次数例如让其循环10次如果还没获取到锁就进入阻塞状态。但是JDK采用了更聪明的方式——适应性自旋简单来说就是线程如果自旋成功了则下次自旋的次数会更多如果自旋失败了则自旋的次数就会减少。 ## 2、锁粗化Lock Coarsening 锁粗化的概念应该比较好理解就是将多次连接在一起的加锁、解锁操作合并为一次将多个连续的锁扩展成一个范围更大的锁。stringBuffer.append(a);stringBuffer.append(b);stringBuffer.append(c); 这里每次调用stringBuffer.append方法都需要加锁和解锁如果虚拟机检测到有一系列连串的对同一个对象加锁和解锁操作就会将其合并成一次范围更大的加锁和解锁操作即在第一次append方法时进行加锁最后一次append方法结束后进行解锁。 ## 3、锁消除 锁消除即删除不必要的加锁操作。根据代码逃逸技术如果判断到一段代码中堆上的数据不会逃逸出当前线程那么可以认为这段代码是线程安全的不必要加锁。看下面这段程序 public static void main(String[] args) {SynchronizedTest02 test02 new SynchronizedTest02();//启动预热for (int i 0; i 10000; i) {i;}long start System.currentTimeMillis();for (int i 0; i 100000000; i) {test02.append(abc, def);} 虽然StringBuffer的append是一个同步方法但是这段程序中的StringBuffer属于一个局部变量并且不会从该方法中逃逸出去所以其实这过程是线程安全的可以将锁消除。对比 锁优点缺点应用场景偏向锁加锁和解锁不需要额外的消耗和执行非同步方法比仅存在纳秒级的差距。如果线程间存在锁竞争会带来额外的锁撤销的消耗。适用于只有一个线程访问同步块场景。轻量级锁竞争的线程不会阻塞提高了程序的响应速度。如果始终得不到锁竞争的线程使用自旋会消耗CPU。追求响应时间。 同步块执行速度非常快。重量级锁线程竞争不使用自旋不会消耗CPU。线程阻塞响应时间缓慢。追求吞吐量。 同步块执行速度较长。 底层实现原理 synchronized 是 JVM 的内置锁基于 Monitor 机制实现。每一个对象都有一个与之关联的监视器 (Monitor)这个监视器充当了一种互斥锁的角色。当一个线程想要访问某个对象的 synchronized 代码块首先需要获取该对象的 Monitor。如果该 Monitor 已经被其他线程持有则当前线程将会被阻塞直至 Monitor 变为可用状态。当线程完成 synchronized 块的代码执行后它会释放 Monitor并把 Monitor 返还给对象池这样其他线程才能获取 Monitor 并进入 synchronized 代码块。 每个Java对象都有一个与之关联的Monitor。这个Monitor的实现是在JVM的内部完成的它采用了一些底层的同步原语用以实现线程间的等待和唤醒机制这也是为什么等待wait和通知notify方法是属于Object类的原因。这两个方法实际上是通过操纵与对象关联的Monitor以完成线程的等待和唤醒操作从而实现线程之间的同步。Synchrpnized和lock的区别 1synchronized是关键字lock是一个类synchronize是在JVM层面实现的发生异常后jvm会释放锁。lock是JDK代码实现的需要手动释放在finally块中释放容易死锁。 2 用法不一样synchronize可以用在代码块上方法上。lock只能写在代码里不能直接修改方法。synchronized在发生异常时会自动释放锁lock需要手动释放锁 3synchronized是非公平锁、可重入锁每个线程获取锁的顺序不是按照线程访问锁的先后顺序获取不可中断锁lock是可公平锁、可中断锁、可重入锁。 4synchronized适用于少量同步lock适用于大量同步。 5锁状态是否可以判断synchronized 不可以lock可以。 ## Synchronized 优点实现简单语义清晰便于JVM堆栈跟踪加锁解锁过程由JVM自动控制提供了多种优化方案使用更广泛 缺点悲观的排他锁不能进行高级功能## Lock 优点可定时的、可轮询的与可中断的锁获取操作提供了读写锁、公平锁和非公平锁   缺点需手动释放锁unlock不适合JVM进行堆栈跟踪## 可重入锁 可重入锁和不可重入锁的最大区别在于可重入锁允许同一个线程在获得锁之后再次获得该锁而不可重入锁不允许。 如果一个线程已经获得锁那么在该线程释放该锁之前它可以再次获得该锁而不会被阻塞。实现原理每次获得锁时计数器加1每次释放锁时计数器减1。只有当计数器为0时其他线程才有机会获得该锁。## ReentrantLock (用于替代synchronized) ReentrantLock提供了可轮询的锁请求。它会尝试着去获取锁如果成功则继续否则可以等到下次运行时处理而synchronized则一旦进入锁请求要么成功要么阻塞所以相比synchronized而言ReentrantLock会不容易产生死锁些。## 公平锁按先来后到的顺序获取锁## 读写锁 ReentrantReadWriteLock 乐观锁 读写锁维护着一对锁一个读锁和一个写锁。通过分离读锁和写锁使得并发性比一般的互斥锁有了较大的提升在同一时间可以允许多个读线程同时访问但是在写线程访问时所有读线程和写线程都会被阻塞。保证多线程的安全性 使用synchronized关键字synchronized关键字可以将某些代码块或方法设为同步代码确保同一时刻只有一个线程可以访问。这种方式需要注意锁的粒度使得锁住的代码块尽可能的短以避免影响程序性能。 使用Volatile关键字Volatile关键字可以用于修饰变量确保多线程之间的可见性即当一个线程修改了共享变量的值其他线程会立即查询最新的值。 使用Lock对象Lock是JDK提供的同步机制Lock提供的Lock()和Unlock()方法可以在同一个时刻只允许一个线程进入执行Lock()和Unlock()方法之间的代码块其他线程必须等待。 使用原子类Java提供了很多原子类包括AtomicInteger、AtomicLong和AtomicBoolean等等这些类可以保证特定操作的原子性避免多线程同时访问一个共享资源所造成的数据安全问题。(atomic是通过CAS实现的) 使用ThreadLocal类ThreadLocal类可以在多线程中为每个线程创建一个独立的实例避免多线程对同一资源的争夺从而保证了数据安全性。 sleep()和wait()的区别 wait()是Object的方法sleep()是Thread类的方法wait()会释放锁sleep()不会释放锁wait()要在同步方法或者同步代码块中执行sleep()没有限制wait()要调用notify()或notifyall()唤醒,sleep()自动唤醒 yield()和join()区别 暂停当前正在执行的线程并执行其他线程。可能没有效果如果成功则调用后线程进入就绪状态告诉当前线程把机会交给其他高优先级的线程join将两个交替执行的线程合并为顺序执行的线程A线程中调用B线程的join() ,则B执行完前A进入阻塞状态使并行的程序串行化执行 Thread、Runable的区别 Thread和Runnable的实质是继承关系没有可比性。无论使用Runnable还是Thread都会new Thread然后执行run方法。用法上如果有复杂的线程操作需求那就选择继承Thread如果只是简单的执行一个任务那就实现runnable。 JAVA内存模型 所有变量都存在主存中主存是线程共享区域每个线程都有自己独有的工作内存线程想要操作变量必须从主从中copy变量到自己的工作区每个线程的工作内存是相互隔离的 ## 栈帧的压入 局部变量表 主要存放了编译期可知的各种数据类型、对象引用。 操作数栈 主要作为方法调用的中转站使用用于存放方法执行过程中产生的中间计算结果。另外计算过程中产生的临时变量也会放在操作数栈中。 动态链接 主要服务一个方法需要调用其他方法的场景。当一个方法要调用其他方法需要将常量池中指向方法的符号引用转化为其在内存地址中的直接引用。动态链接的作用就是为了将符号引用转换为调用方法的直接引用这个过程也被称为 动态连接## 栈帧的弹出 Java 方法有两种返回方式一种是 return 语句正常返回一种是抛出异常。不管哪种返回方式都会导致栈帧被弹出。也就是说 栈帧随着方法调用而创建随着方法结束而销毁。无论方法正常完成还是异常完成都算作方法结束。
http://www.zqtcl.cn/news/328639/

相关文章:

  • 有哪些网站做的很好桐乡市建设局官方网站
  • 做公众号和网站一样吗免费正能量网站下载ww
  • 使用帝国做软件下载网站源码顺义区做网站的公司
  • 网站用什么颜色成都网站建设公司服务
  • 重庆手机网站方案设计凡科建站网站怎么保存发给别人
  • 北京住房建设官方网站xampp wordpress服务器
  • 卖衣服的网站建设素材网站免费短视频
  • 萍乡网站建设哪家公司好搜索引擎推广预算
  • 如何做网站不容易被攻击网站定位策划书
  • 自己做网站去哪买服务器多说wordpress
  • 网站排名突然没有了网站开发背景图
  • 比较容易做流量的网站设计模板素材网站
  • 电商网站建设 网站定制开发兰州展柜公司网站建设
  • 临沂城乡建设管理局网站腾讯体育
  • 一个空间怎么放两个网站ps个人网站首页怎么制作
  • 云南通耀建设工程有限公司网站国际购物网站平台有哪些
  • 网站建设外包服务上海网站建设公司怎么分辨好坏
  • 建筑类企业网站模板下载微信搜索推广
  • 上海网站备案在哪里wordpress短链接关键字
  • 金诚财富网站是谁做的建站技术博客
  • 黔东南网站设计公司儿童衣服刘涛做代言那个是什么网站
  • 网站首页样式百度推广是什么工作
  • 广告手机网站制作阿里云域名 设置网站
  • 杭州市拱墅区网站建设ui培训有用么
  • 广东手机网站建设多少钱邯郸市做网站的公司
  • seo网站优化推广怎么做龙岗中心医院
  • 建网站程序智能网站建设平台
  • 建筑公司分几级资质seo入门培训
  • wap类网站上海网站建设免费推
  • 网站建设哪家好公司建设银行网站怎么登陆不