1.ThreadLocal用法详解和原理
1.threadLocal称为线程本地变量,可以通过ThreadLocal为变量在线程中创建一个副本,这样可使线程独立访问自己的内部变量;
2.threadLocalMap是Thread类的成员变量,同时也是threadLocal的内部类,可以通过threadLocal的set或get方法初始化threadLocals,副本变量以键值对的方式保存在 threadLocals中,其中key为threadLocal,value为要保存的副本变量;
3.好处是:为每个线程提供一个独立的副本变量,可以是线程之间对变量的修改互不影响。
友情链接:ThreadLocal用法详解和原理
2.volatile关键字解析
1)保证了不同线程对这个变量进行操作时的可见性,即一个变量修改了某个值,这个新值对其他线程是立即可见的;
2)禁止进行指令重排
友情链接:volatile关键字解析
3.java创建线程的三种方式及其对比
友情链接:java创建线程的三种方式及其对比
4.Runnable接口和Callable接口的区别
相同点:
1.两者都是接口;(废话);2.两者都可用来编写多线程程序;3.两者都需要调用Thread.start()启动线程;
不同点:
1.实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;
2.Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不继续上抛;
注意:Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取将来结果,当调用不到此方法时,主线程不会阻塞
5.创建线程池的4种方式
线程池的作用: 在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。
常用线程池:ExecutorService 是主要的实现类,其中常用的有 Executors.newSingleThreadPool(),newFixedThreadPool(),newcachedTheadPool(),newScheduledThreadPool()。
- 如果当前池大小 poolSize 小于 corePoolSize ,则创建新线程执行任务。
- 如果当前池大小 poolSize 大于 corePoolSize ,且等待队列未满,则进入等待队列
- 如果当前池大小 poolSize 大于 corePoolSize 且小于 maximumPoolSize ,且等待队列已满,则创建新线程执行任务。
- 如果当前池大小 poolSize 大于 corePoolSize 且大于 maximumPoolSize ,且等待队列已满,则调用拒绝策略来处理该任务。
- 线程池里的每个线程执行完任务后不会立刻退出,而是会去检查下等待队列里是否还有线程任务需要执行,如果在 keepAliveTime 里等不到新的任务了,那么线程就会退出。
ThreadPoolExecutor
corePoolSize:核心线程池大小;maximumPoolSize:最大线程池大小;keepAliveTime:线程最大空闲时间;TimeUnit:时间单位; handler:拒绝策略
友情链接:创建线程池的4种方式
6.Callable、Future和FutureTask
友情链接:Callable、Future和FutureTask
7.cas 无锁技术
1.数据库有两种锁,悲观锁的原理是每次实现数据库的增删改的时候都进行阻塞,防止数据发生脏读;乐观锁的原理是在数据库更新的时候,用一个version字段来记录版本号,然后通过比较是不是自己要修改的版本号再进行修改。这其中就引出了一种比较替换的思路来实现数据的一致性,事实上,cas也是基于这样的原理。
2.cas的英文翻译全称是compare and set ,也就是比较替换技术,·它包含三个参数,CAS(V,E,N),其中V(variile)表示欲更新的变量,E(Excepted)表示预期的值,N(New)表示新值,只有当V等于E值的时候吗,才会将V的值设为N,如果V值和E值不同,则说明已经有其它线程对该值做了更新,则当前线程什么都不做,直接返回V值。
举个例子,假如现在有一个变量int a=5;我想要把它更新为6,用cas的话,我有三个参数cas(5,5,6),我们要更新的值是5,找到了a=5,符合V值,预期的值也是5符合,然后就会把N=6更新给a,a的值就会变成6;
3.cas是以乐观的态度运行的,它总是认为当前的线程可以完成操作,当多个线程同时使用CAS的时候只有一个最终会成功,而其他的都会失败。这种是由欲更新的值做的一个筛选机制,只有符合规则的线程才能顺利执行,而其他线程,均会失败,但是失败的线程并不会被挂起,仅仅是尝试失败,并且允许再次尝试(当然也可以主动放弃)
4.cas可以发现其他线程的干扰,排除其他线程造成的数据污染
ABA:如果另一个线程修改V值假设原来是A,先修改成B,再修改回成A。当前线程的CAS操作无法分辨当前V值是否发生过变化。
关于ABA问题我想了一个例子:在你非常渴的情况下你发现一个盛满水的杯子,你一饮而尽。之后再给杯子里重新倒满水。然后你离开,当杯子的真正主人回来时看到杯子还是盛满水,他当然不知道是否被人喝完重新倒满。解决这个问题的方案的一个策略是每一次倒水假设有一个自动记录仪记录下,这样主人回来就可以分辨在她离开后是否发生过重新倒满的情况。这也是解决ABA问题目前采用的策略。友情链接:cas 无锁技术,Java CAS 和ABA问题
8.synchronized与Lock的区别
1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
友情链接:synchronized与Lock的区别
9.分布式环境下,怎么保证线程安全?
友情链接:分布式环境下,怎么保证线程安全?
10.线程的五大状态
友情链接:线程的五大状态
11.sleep() wait() yield() join()用法与区别
友情链接:sleep() wait() yield() join()用法与区别
12.ABC三个线程如何保证顺序执行
在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。
13.volatile和synchronized的区别
- volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
- volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
- volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
- volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化