他的回复:
华为云账号:hw1647796330 第五周笔记线程的死锁当一个线程永远地持有一个锁,并且其他线程都尝试去获得这个锁时,那么它们将永远被阻塞。如果线程A持有锁L并且想获得锁M,线程B持有锁M并且想获得锁L,那么这两个线程将永远等待下去,这种情况就是最简单的死锁形式。线程的明锁在java5中,专门提供了锁对象Lock,利用锁可以方便的实现资源的封锁,用来竞争资源并发访问的控制。Lock所有加锁和解锁的方式都是显式的。Lock.lock():获取锁 Lock.unlock()Lock与synchronize对比:1.lock不是java语言内置的,synchronize是java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问。2.synchronized不需要手动释放锁,当synchronized方法或者synchronized代码执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。Java的ReenTrantLock也就是用队列实现的公平锁和非公平锁在公平的锁中,如果有另一个线程持有或者有其他线程在等待队列中等待这个线程,那么新发出的请求的线程将被放入到队列中。而非公平锁上,只有当被某个线程持有时,新发出的线程才会被放入队列中,(此时和公平锁是一样的)所以二者的差别在于非公平锁会有更多的机会去抢占锁。线程通信机制线程间同步可以归纳为线程间通信的一个子集,对于线程通信指的是两个线程之间可以交换一些实时的数据信息。线程是操作系统中独立的个体,但这些个体如果不经过特殊处理就不能成为一个整体,线程间的通信就成为整体的必用方式之一。当线程存在通信指挥,系统间的交互性会更强大,在提高CPU利用率的同时还会使开发人员对线程任务在处理过程中进行有效的把控与监督。wait/notify方法实现线程间的通信: 1.等待/通知机制,是指线程A调用了对象的wait()方法进入等待状态,而线程B调用了对象的notify()或notifyall方法,线程A收到通知后退出等待队列,进入可运行状态,进而知心后续操作。 2.从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其他线程调用对象的notify唤醒该线程,才能继续获取对象锁,并继续执行。 3.wait和notify方法是Object类的方法Lock和Condition通信机制 1.Lock用于控制多线程对需要竞争的贡献资源的顺序访问,保证该状态的连续性。 2.Condition是java提供的来实现等待/通知的类,condition对象是由Lock对象所创建的。 3.condition中的await方法相当于Object的wait方法,condition中的signal方法相当于object的notify方法。线程的ThreadLocal本地缓存对象ThreadLocal线程范围内的共享变量: 线程范围内的共享变量,每个线程只能访问自己的数据,不能访问别的线程数据。每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,可以分别是各自的线程,value是各自的set方法穿进去的值。线程的volatile关键字volatile挂你尖子可以用来修饰字段(成员变量),就是告知程序任何对该变量的访问均需要从共享内存中获取,而对它的改变必须同步刷新回共享内存,它能保证所有线程对变量访问的可见性。volatile的作用:使变量在多个线程间可见,但是无法保证原子性。 需要注意的是一般volatile用于只针对多个线程可见的变量操作,并不能代替synchronize的同步功能。线程池的作用: 1.降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗 2.提高影响速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。 3.提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会减低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。线程池的应用: 场景:请求频繁,考虑到服务的并发问题,如果每个请求来到后,服务都为它启动一个线程,那么这对服务的资源可能会造成很大的浪费。线程的同步工具类CountDownLatch同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 1.CountDownLatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用countDown方法,计数器减一,计数器大于0时,await方法会阻塞程序继续执行。 2.由于调用了countDown方法,所以在当前计数到大零之前,await方法会一直受阻塞。之后,会释放所有等待的线程,await的所有后续调用都将立即返回。这种现象只出现一次,计数无法被重置。一个线程或者多个,等待另外N个线程完成某个事情之后才能执行。CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点。因为该barrier在释放等待线程后可以重用,所以称它为循环的barrier。Semaphore是一个计数信号量,他的本质是一个共享锁,是基于AQS实现的,通过state变量来实现共享。通过调用acquire方法,对state值减去一,当调用release的时候,对state值加一。当state变量小于0的时候,在AQS队列中阻塞等待。Exchanger是一个用于线程间协作的工具类,Exchanger用于进行线程间的数据交换;它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据;这两个线程通过exchange交换数据。如果第一个线程先执行exchange,它会一直等待第二个线程也执行exchange,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。线程的fork/join机制fork/join框架是java7提供一个用于并执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。分治法:把一个规模大的问题划分为规模较小的子问题,然后分而治之,最后合并子问题的解得到原问题的解。synchronize和volatile区别: 1.volatile关键字解决的是变量在多个线程之间的可见性;而synchronize关键字解决的是多个线程之间访问共享资源的同步性。 2.volatile只能用于修饰变量,而synchronize可以修饰方法,以及代码块 3.多线程访问volatile不会发生阻塞,而synchronize会出现阻塞。 4.volatile能保证变量在多个线程直接爱你的可见性,但不能保证原子性;而synchronize可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公有内存中的数据做同步。synchronize和Lock区别: 1.Lock是一个接口,而synchronize是java中的关键字,synchronize是内置的语言实现。 2.synchronize在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock去释放锁,则很可能造成死锁现象,因此时使Lock时需要在finally块中释放锁。 3.Lock可以提高多个线程进行读操作的效率(读写锁)。线程的读写分离机制ReadWriteLock顾名思义是读写锁,它维护了一对相关的锁“读取锁”和“写入锁”,一个用于读取操作,另一个用于写入操作。读取所用于只读操作,它是共享锁,能同时被多个线程获取。写入锁用于写入操作,它是独占锁,写入锁只能被一个线程获取。不能同时存在读取锁和写入锁,可以读-读,但不能读-写,写-写。