多线程知识点记录①
1.多个线程多把锁,锁有分对象锁和类锁,类锁就是在同步函数上加static关键字。
2.两个线程访问一个对象锁的两个加锁方法,要等一个线程释放锁后两个线程才能拿到对象所进行另一个方法的访问。
3.子类调用父类中的同步方法,线程也是安全的。
4.不要用字符串加锁。
5.把一个对象作为锁,里面的属性值变化是不影响锁的。
6.每开一个线程,每个线程都会有一个本线程的内存空间,在类中用到的成员变量都会装到这个内存中,并且直接引用这个内存中的变量,
所以修改变量时不会修改到这个内存中的值,但是类中还是直接用的这个内存中的值,但是如果变量加volatile时,变量被修改后子线程就会
直接到主线程里取读数据,而不会再去自己的内存中读数据了。
7.加volatile只是有可见性,但是没有原子性,也就是线程是不安全的,如果要保证线程安全可以用automicInteger等api,automic也只是保证
本方法中的同步,多线程之间也是线程不安全的,所以多线程进行访问的话要加synchronized。
8.wait和notify必须配合synchronize使用,不管几个线程都用的同一个锁,一个线程任务拿到锁,其他的线程任务就得堵塞等待,wait释放锁,notify不释放锁。因notify不马上释放锁,所以不能做到实时的效果,所以可以使用
Countdownlatch,await,countdown(也是不释放执行权),它相当于wait,notify的升级版。
9.ThreadLocal修饰的参数,不是每个线程公用的,而是每个线程都有自己的一块空间,各不干扰。
10.多线程下的单例可以用静态内部类。
11.同步类容器虽然也是线程安全的,但是效率不高。
12.并发类容器有current接口下的一些实现类,比如currentHashMap,CopyOnWrite,CopyOnWrite在添加元素之前会复制出一个新的容器,元素都
存到新容器下,旧容器用于查询,做到读写分离了。CopyOnWrite在读多写少的情景下用的多。
13.队列有分无堵塞队列和堵塞队列,无堵塞队列是concurrentLinkedQueue,堵塞队列有ArrayBlockingQueue--数值结构,有界队列,LinkedBlockingQueue
--链表结构,无界队列,synchronizeQueue,队列大小为空,只有在有请求堵塞的情况下,添加才会成功,添加不是直接添加到队列中,而是直接赋给
正在请求的堵塞线程。PriorityBlockingQueue,存到队列中的对象都必须实现comparable接口,是一个无界队列,DelayBlockingQueue,存到队列中的对象都
必须实现Delayed接口,延迟时间到才能从队列中取出对象。
14.线程池有分FixedThreadPool--固定大小线程池,CachedThreadPool--缓冲线程池,线程池中的队列无界,可以一直添加任务,初始化核心
线程数为0,队列是一个synchronizeQueue队列。SingleThreadExcutor--只有一个线程,队列是无界队列,创建一个单线程化的线程池,它只会用唯一 的工作线程来执行任务,保证所有任务按照指定顺序(FIFO)执行,如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行,ScheduledThreadPool,定时线程池,类似Timer,线程中的线程都是一个定时任务,scheduleWithFixedDelay和scheduleAtFixedRate的区别是前者无论执行时间是否大于还是小于定时时间,都要延迟到指定的间隔时间再执行,后者是如果执行时间大于间隔时间,当前任务执行则马上进入下一个循环任务。线程池submit和execute的区别一个是submit有返回值。另一个是submit可以接收到异常。第三个是传递的参数不一样,submit可以接收实现Callable接口的线程任务。
15.Java容器总结,linked是链表结构,增删快,查询慢,tree是二叉树结构,有序的,hash是hash表结构,是唯一性,不可重复的,array是数组结构的,查询快,增删慢。
除了vector是线程同步的,其他都是不同步的,但是可以通过collections集合工具类进行同步转换。collection和map接口,collction下有两个set和list接口,
set下有实现类:haseset和treeset,linkedhashset,list下有arraylist和linkedlist,map下有treemap和hashmap,linkedhashmap。
16.concurrent接口下的一些工具类:countDownLatch-->因为notify没有马上释放锁,所以需要的马上释放锁的业务场景下不能满足,因此可以使用CountDownLatch.await,
CountDownLatch.countDown进行释放。
17.实现Callable接口是为了条用threadPool.submit可以返回数据。
18.重入锁的意思是可以进入获取的对象锁的其他方法或者属性。
19.线程状态:新建,就绪,运行,(堵塞,等待,睡眠,都是有锁没执行权的),死亡。
20.yield:如果你觉得一个线程不是那么重要,或者优先级非常低,而且又害怕它会占用太多的CPU资源,那么可以在适当的时候调用Thread.yield(),
给予其他重要线程更多的工作机会。线程调用该方法后会把执行权让给相同优先级的其他线程。
21.shutdown和shutdownNow都是停止线程池的新任务提交,区别是shutdown会等线程池中的任务和正在运行的线程运行完才会。而shutdownNow则会清空线程池中的任务,并且
尝试用interrupt中断所有正在执行的线程。
22.线程stop和interrupt的区别是stop强制中断线程,现在已经不提倡用这个,而interrupt是会先把该关的资源先关掉,在进行中断。
23.用户线程和守护线程的区别:当还有用户线程在运行时,jvm不能关闭,不管是否有守护线程,jvm都可以关闭,像垃圾回收的线程就是守护线程。
24.join,wait,sleep,yiled,堵塞都是进入就绪状态,堵塞是被动的,其他是主动的,而且他们都是释放执行权,但是没有释放锁.。
25.synchronize和reentrantlock的区别有:1.在锁没被释放前,其他的线程属于堵塞状态,有可能造成死锁,ReentranceLock可以中断这种堵塞而去做其他的工作。2.ReentranceLock可以设置公平所,按线程请求的前后顺序进行获取锁。3.ReentranceLock可以有多个condition,更加灵活的使用多线程。
26.
乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。
悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。
从上面的描述我们可以看出,悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升。
悲观锁在Java中的使用,就是利用各种锁。
乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。
27.自旋锁:因为一个线程在等待锁的时候是堵塞的,而如果要阻塞或唤醒一个线程,都需要操作系统来帮忙完成,所以如果上一个线程是马上能释放锁的,并且锁的竞争不激烈,就可以用自旋锁而不必堵塞消费系统资源。
28.interrupt()只是设置中断标识位为true,具体还需要根据代码控制进行中断,在线程时堵塞状态的时候(例如sleep/wait/join/io/同步的时候),进行interrupt会使状态位重新设置为false,并且会抛出interrupteexception的异常,通过捕捉异常做相应的中断后的处理,interrupted只在本线程有用,并且会将状态位重新设置为false,isinterrupted可以调用其他线程的isinterrupted方法,只会返回状态,不会修改状态
29.synchronized锁重入的意思是当拿到一个对象的锁时,也可以执行用该锁的其他的方法。就是说可以执行该锁对象的所有方法。
30.脏读的意思是当一个线程在修改一个公共方法的时候,这个时候另一个线程去获取这个公共方法的值,这样就会造成脏读。
31.造成死锁的原因可能是a、b两个锁,同步代码块中的代码又调用了另外一个的锁的代码,两个都在等对方释放锁,从而造成的死锁。
32.volatile是保证多线程能对主线程变量的一致可见性,但是不是原子性的,也就是不是同步的,线程不安全的,所以一般用在Boolean的true和false下,比如初始值是true,只要有线程将他设成false,则停止业务操作,所有就算有多个线程同时设成false,也不会影响什么。AutomicInteger就可以保证线程安全,具有可见性,一般在高并发下使用。
33.volatile的原理是jvm设置每个线程都有自己的一块内存区域,将共享内存的变量值复制了一份在自己的内存区域里,然后每次都到自己的内存区域取,加了volatile之后就会强制子线程到主线程去取值。
34.比如实例化100个线程,每个线程的变量和方法只有自己能访问,共享变量可以直接在线程中写,所有线程公用,加static修饰符。
35.线程越多,cpu需要来回切换,来回切换花的时间可能会大于,较少线程,一次执行的时间久一点来的更长,所以线程越多,并不是并行处理更快。