下面的学习笔记参考狂神说java的博客
线程和进程
进程:一个程序,QQ.exe Music.exe 程序的集合
一个进程往往可以包含多个线程,至少包含1个线程
java默认有几个线程? 2个mian,GC
线程:开了一个进程 Typora,写字,自动保存(线程负责的)
对于Java而言 Thread,Runnable,Callable
java真的可以开启线程吗? 开不了的
java无法直接操作硬件
并发,并行
并发(多线程操作同一资源)
>CPU1核,模拟出多条线程,天下武功,唯快不破,快速交替
并行(多个人一起行动)
>CPU 多核,多个线程可以同时执行.线程池
并发编程的本质:充分利用CPU的资源
所有的公司都很看重
企业,挣钱=>提高效率,裁员
人员(减),技术成本(提高)
线程有几个状态
NEW 新生
RUNNABLE 运行
WAITING 等待
TIMED_WAITING 超时等待
TERMINATED 终止
wait/sleep 区别
1.来自不同的类
wait=> Object
sleep=> Thread
2.关于锁的释放
wait会释放锁,sleep睡觉了,抱着锁睡觉,不会释放!
3.使用的范围是不同的
wait:必须再同步代码块中
sleep可以再任何地方睡
4.是否需要捕获异常
wait不需要捕获异常
sleep必须捕获异常
3.Lock锁(重点)
公平锁:十分公平:可以先来后到
非公平锁:十分不公平:可以插队 (默认)
Synchronized 和 Lock 区别
1.Synchronized 内置的java关键字,Lock是一个java类
2.Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
3.Synchronized 会自动释放锁,lock必须要手动释放锁! 如果不释放锁,会死锁
4.Synchronized 线程 1 (获得锁,阻塞),线程2(等待,傻傻的等) Lock锁就不一定会一直等待
5.Synchronized 可重入锁,不可以中断,非公平,Lock 可重入锁,可以判断锁,非公平(可设置公平)
6.Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码
锁是什么,任何判断锁的是谁!
4.生产者和消费者问题
通过Lock 找到Condion
5.8锁现象
如何判断锁的是谁?永远的知道什么是锁,锁到底锁的是谁!
1.两个同步方法,都没延迟,就是先来后到
2. 两个同步方法,对第一个增加延迟并不会改变执行顺序,排队
3. 同时执行 一个普通方法,和一个同步方法,先来后到,再比较谁延迟谁后发出,不排队
4. 两个对象分别执行一个方法,这两个同时执行,所以延迟的会后面发出,不排队
5.一个对象 同时执行两个静态同步方法,延迟的还是会先执行,要排队
6.两个对象,分别执行两个静态同步方法,延迟的还是会先执行,要排队(虽然两个对象,但是还是要排队)
7.1个对象,执行一个静态带延迟的同步和一个普通同步,普通同步先执行(不排队)
8.2个对象,分别执行个静态带延迟的同步和一个普通同步,普通同步先执行(不排队)
小结:
new this 具体的一个手机
static Class 唯一的一个模板
同步方法加了一个static后,他就像一个新的对象,只和且一定加static的比较
集合类不安全
List多线程不安全
//并发下 ArrayList 不安全
/**
* 解决方案:
* 1.List<String> list=new Vector<>();
* 2.List<String> list= Collections.synchronizedList(new ArrayList<>());
* 3.List<String> list= new CopyOnWriteArrayList<>();
*
*/
//CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略
//多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
//在写入的时候避免覆盖,造成数据问题
//读写分离 MyCat
//CopyOnWriteArrayList 比Vector 牛在哪里
//CopyOnWriteArrayList底层使用lock Vector在多线程的时候synchronized,效率低
List<String> list= new CopyOnWriteArrayList<>();
for(int i=0;i<=10;i++){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
Set多线程不安全
//Set<String> set=new HashSet<>();
//HashSet的底层是HashMap
//解决方案1:Set<String> set= Collections.synchronizedSet(new HashSet<>());
//解决方案2:Set<String> set=new CopyOnWriteArraySet<>();
Set<String> set=new CopyOnWriteArraySet<>();
for(int i=1;i<=30;i++){
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
hashSet的底层是HashMap
Map多线程不安全
//map是这样用的么? 不是,工作中不用 HashMap
// 默认等价于什么? new HashMap<>(16,0.75);
//HashMap<String,String> map=new HashMap<>();
Map<String,String> map=new ConcurrentHashMap<>();
// 加载因子 ,初始化容量
for(int i=1;i<=30;i++){
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
}, String.valueOf(i)).start();
}
Callable
Callable类似Runnable,Runnable不返回结果,也不能抛出异常
有缓存
结果可能需要等待,会阻塞
public class CallableTest {
public static void main(String[] args) throws Exception{
// new Thread(new Runnable()).start();
// new Thread(new FutureTask<V>()).start();
// new Thread(new FutureTask<V>( Callable )).start()
new Thread().start();//怎么启动Callable
MyThread thread=new MyThread();
//适配类
FutureTask futureTask=new FutureTask(thread); //适配类
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();//结果会被缓存,效率高
Integer ov=(Integer) futureTask.get();//这个get方法 可能产生阻塞,把他放到最后一行
System.out.println(ov);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call(){
System.out.println("call()");
//耗时的操作
return 1024;
}
}
常用的辅助类
1.CountDownLatch
这个类使一个线程等待其他线程各自执行完毕后再执行(减法计数器)
public class CountDownLatchDemo {
public static void main(String[] args) throws Exception{
//倒计时是6,必须要执行任务的时候,再使用
CountDownLatch countDownLatch=new CountDownLatch(6);//如果为7,就会一直等待
for(int i=1;i<=6;i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"Go Out");
countDownLatch.countDown();//数目-1
},String.valueOf(i)).start();
}
countDownLatch.await();//等待计数器归零,然后再向下执行
System.out.println("Close Door");
}
}
2.CycliBarrier
它的作用就是会让所有线程都等待完成后才会继续下一步行动
public class CyclicBarrierDemo {
public static void main(String[] args){
/**
* 集齐7颗龙珠召唤神龙
*/
//召唤龙珠的线程
CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{//如果 将7改为6,呢么在输出6.收集6个龙珠,就召唤神龙成功,然后再7,收集7个龙珠
System.out.println("召唤神龙成功"); //如果 将7改为8,呢么会一直等待
});
for(int i=1;i<=7;i++){
final int temp= i;
new Thread(()->{
//lamdba能操作到i么
System.out.println(Thread.currentThread().getName()+",收集"+temp+"个龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
3.Semaphore
Semaphore 是一个计数信号量,必须由获取它的线程释放。
常用于限制可以访问某些资源的线程数量,例如通过 Semaphore 限流
public class SemaphoreDemo {
public static void main(String[] args) {
//线程数目:停车位,限流!
Semaphore semaphore=new Semaphore(3);
for(int i=1;i<=6;i++){
new Thread(()->{
//acquire() 得到
try {
semaphore.acquire();//获取
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
}
finally {
semaphore.release();//释放
}
},String.valueOf(i)).start();
}
}
}
读写锁
ReadWriteLock 读写锁
读的时候可以多个线程读,写的时候只能1个线程写
读写所允许同一时刻被多个读线程访问,但是在写线程访问时,所有的读线程和其他的写线程都会被阻塞
/**
* 独占锁(写锁) 一个只能被一个线程占有
* 共享锁(读锁) 多好线程可以同时占有
* 读-读 可以共存!
* 读-写 不能共存!
* 写-写 不能共存!
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache=new MyCache();
MyCacheLock myCacheLock=new MyCacheLock();
//写入
for(int i=1;i<=10;i++){
final int temp=i;
new Thread(()->{
myCacheLock.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
//读取
for(int i=1;i<=10;i++){
final int temp=i;
new Thread(()->{
myCacheLock.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCacheLock{
private volatile Map<String,Object> map=new HashMap<>();
//读写锁,更加细粒度的控制
private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
//存,写入的时候,只希望同时只要一个线程写
public void put(String key,Object value){
readWriteLock.writeLock().lock();//写锁
try {
System.out.println(Thread.currentThread().getName()+"写入"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入OK");
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.writeLock().unlock();
}
}
//取,读,所以人都可以读
public void get(String key){
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o=map.get(key);
System.out.println(Thread.currentThread().getName()+"读取OK");
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.readLock().unlock();
}
}
}
阻塞队列 BlockingQueue
方式 | 抛出异常 | 有返回值 | 阻塞 | 超时等待 |
添加 | add() | offer() | put() | offer(,,) |
移除 | remove() | poll() | take() | poll(,) |
检测队首 | element() | peek() | - | - |
public static void main(String[] args) throws Exception{
//Collection: {List,SetBlockingQueue}
test4();
}
public static void test1(){
//需要写队列的大小
ArrayBlockingQueue blockingQueue=new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
//抛出异常: java.lang.IllegalStateException: Queue full
//System.out.println(blockingQueue.add("d"));
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
}
public static void test2(){
ArrayBlockingQueue blockingQueue=new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("d"));
System.out.println(blockingQueue.peek());
System.out.println(blockingQueue.element());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
}
public static void test3() throws InterruptedException{
/**
* 等待,阻塞(一直阻塞)
*/
ArrayBlockingQueue blockingQueue=new ArrayBlockingQueue<>(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//blockingQueue.put("d");//队列没有位置,会一直等待
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());//没有元素,会一直等待
}
public static void test4() throws InterruptedException{
/**
* 等待,阻塞(等待超时)
*/
ArrayBlockingQueue arrayBlockingQueue=new ArrayBlockingQueue<>(3);
System.out.println("1");
arrayBlockingQueue.offer("a");
arrayBlockingQueue.offer("b");
arrayBlockingQueue.offer("c");
arrayBlockingQueue.offer("d",2, TimeUnit.SECONDS);//阻塞,等待2秒就退出
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll(2,TimeUnit.SECONDS));
System.out.println("3");
}
同步队列 SynchronousQueue
/**
* 同步队列
* 容量为1,同时只能存在一个
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
SynchronousQueue<String> synchronousQueue=new SynchronousQueue<String>();//同步队列
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" put 1");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName()+" put 2");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName()+" put 3");
synchronousQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+" => "+synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+" => "+synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+" => "+synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
}
}
线程池
池化技术
程序的运行,本质:占用系统的资源!优化资源的使用! => 池化技术
线程池,连接池,内存池,对象池..........
池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完还给我.
线程池的好处:
1.降低资源的消耗
2.提高响应的速度
3.方便管理
线程复用,可以控制最大并发数,管理线程
线程池:{三大方法,7大参数,4种拒绝策略}
三大方法:
// Executors 工具类:3大方法
//使用线程池之后,要使用线程池 创建线程
public class Demo1 {
public static void main(String[] args) {
//ExecutorService threadPool=Executors.newSingleThreadExecutor(); //单个线程
ExecutorService threadPool=Executors.newFixedThreadPool(5); //创建固定的线程池的大小
//ExecutorService threadPool=Executors.newCachedThreadPool();//创建一个可伸缩的线程池
try {
for(int i=0;i<10;i++){
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
}finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//21亿
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
三个方法均调用了ExecutorService
7大参数
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,//最大核心线程池大小
long keepAliveTime,//超时了没有调用就释放
TimeUnit unit,//超时单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂,一般不用动
RejectedExecutionHandler handler) {//拒绝策略
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,避免资源耗尽的奉献
手动创建一个线程池
// Executors 工具类:3大方法
//使用线程池之后,要使用线程池 创建线程
/**
* ExecutorService threadPool=Executors.newSingleThreadExecutor(); //单个线程
* ExecutorService threadPool=Executors.newFixedThreadPool(3);//创建固定的线程池的大小
* ExecutorService threadPool=Executors.newCachedThreadPool();//创建一个可伸缩的线程池
*/
public class Demo1 {
public static void main(String[] args) {
//自定义线程池! 工作 ThreadPoolExecutor
int maxCPU=Runtime.getRuntime().availableProcessors();//获取cpu的线程数
// 最大线程到底该如何定义
// 1.CPU密集型 ,几核就定义为几,保持cpu效率最高
// 2.IO密集型 判断你的任务中十分耗IO的线程
ExecutorService threadPool=new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
//new ThreadPoolExecutor.AbortPolicy());//银行满了,还有人进来,不处理这个人,抛出异常
//new ThreadPoolExecutor.CallerRunsPolicy());//只要线程池未关闭,该策略直接在调用者线程中运行当前被丢弃的任务
//new ThreadPoolExecutor.DiscardPolicy());//队列满了,不允许多的任务,丢掉多的任务
new ThreadPoolExecutor.DiscardOldestPolicy());//队列满了,丢掉任务,不会抛出异常,
try {
//最大承载数:BlockingDeque +max
for(int i=1;i<=9;i++){
//使用线程池创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
}finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
4种拒绝策略
new ThreadPoolExecutor.AbortPolicy());//银行满了,还有人进来,不处理这个人,抛出异常
new ThreadPoolExecutor.CallerRunsPolicy());//只要线程池未关闭,该策略直接在调用者线程中运行当前被丢弃的任务
new ThreadPoolExecutor.DiscardPolicy());//队列满了,不允许多的任务,丢掉多的任务
new ThreadPoolExecutor.DiscardOldestPolicy());//队列满了,丢掉任务,不会抛出异常,
线程池最大的大小该如何去设置? => IO密集型,cpu密集型 :(调优)
四大函数式接口
新时代的程序员必会:lambda表达式,链式编程,函数式接口,Stream流式计算
函数式接口:只有一个方法的接口
@FunctionalInterface public interface Runnable { public abstract void run(); }
// 泛型、枚举、反射
// lambda表达式、链式编程、函数式接口、Stream流式计算
// 超级多FunctionalInterface
// 简化编程模型,在新版本的框架底层大量应用!
// foreach(消费者类的函数式接口)
Function函数式接口
/**
* Function 函数式接口,只有一个输入参数,有一个输出
* 只要是 函数型接口 可以 用 lambda表达式简化
*/
public class Demo01 {
public static void main(String[] args) {
//工具类:输出输入的值
Function function=new Function<String,String>() {
@Override
public String apply(String str) {
return str;
}
};
Function function1=(str)->{return str;};//用 lambda表达式简化 和上面的等级
System.out.println(((Function<String, String>) function).apply("aaa"));
System.out.println(function1.apply("bbb"));
}
}
predicate函数式接口
/**
* 断定型接口: 有一个输入参数,返回值只能是 布尔值
*/
public class Demo02 {
public static void main(String[] args) {
//判断字符串是否为空
Predicate<String> predicate=new Predicate<String>() {
@Override
public boolean test(String str) {
return str.isEmpty();
}
};
System.out.println(predicate.test(""));
Predicate<String> predicate1=(str)->{return str.isEmpty();};
}
}
Consumer 消费型接口
/**
* 消费型接口
* 只有输入,没有返回值
*/
public class Demo03 {
public static void main(String[] args) {
Consumer<String> consumer=new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
Consumer<String> consumer1=(str)->{
System.out.println(str);
};
consumer1.accept("gg");
}
}
Supplier 供给型接口
/**
* 供给型接口
* 没有参数,只有返回值
*/
public class Demo04 {
public static void main(String[] args) {
Supplier<Integer> supplier=new Supplier<Integer>() {
@Override
public Integer get() {
return 1024;
}
};
Supplier<Integer> supplier1=()->{return 1024;};
System.out.println(supplier1.get());
}
}
Stream流式计算
什么是Stream流式计算?
大数据存储+计算
存储:集合,MySQL 本质就是存储
计算都应该交给流操作!
/**
* 题目要求:只用一行代码实现
* 1.ID必须为偶数
* 2.年龄必须大于23
* 3.用户名转化为大写字母
* 4.用户名字母倒着排序
* 5.只输出一个用户!
*/
public class Test {
public static void main(String[] args) {
User user1=new User(1,"a",21);
User user2=new User(2,"b",22);
User user3=new User(3,"c",23);
User user4=new User(4,"d",24);
User user5=new User(5,"e",25);
List<User> list= Arrays.asList(user1,user2,user3,user4,user5);
//Stream流,链式编程,函数式接口
list.stream()
.filter(u->{return u.getId()%2==0;})
.filter(u->{return u.getAge()>23;})
.map(u->{return u.getName().toUpperCase();})
.sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
.limit(1)
.forEach(System.out::println);
}
}
ForkJoin
ForkJoin在JDK 1.7 并行执行任务!提高效率,大数据量
ForkJoin特点:工作窃取(这里维护了一个双端队列)
/**
* 求和计算
* 如何使用forkjoin
* 1.forkjoinPool 通过他来执行
* 2.计算任务 forkjoinpool.execute(ForkjoinTask task)
* 3.计算类继承ForkjoinTask
*/
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
private Long temp=10000L;
public ForkJoinDemo(Long start,Long end){
this.start=start;
this.end=end;
}
public static void main(String[] args) {
}
@Override
protected Long compute(){
if(end-start<temp){//用分支合并计算
Long sum=0L;
for(Long i=start;i<=end;i++){
sum+=i;
}
return sum;
}else{//很像递归
long mid=(start+end)/2;//中间值
ForkJoinDemo task1=new ForkJoinDemo(start,mid);
task1.fork();//拆分任务,把任务压入线程队列
ForkJoinDemo task2=new ForkJoinDemo(mid+1,end);
task2.fork();
return task1.join()+task2.join();
}
}
}
测试:
public class Test {
public static void main(String[] args) throws Exception{
test3();
}
public static void test1(){
long start=System.currentTimeMillis();
Long sum=0L;
for (Long i=1L;i<=10_0000_0000;i++){
sum+=i;
}
long end=System.currentTimeMillis();
System.out.println("sum="+sum+",时间:"+(end-start));
}
public static void test2() throws Exception{//快百分之10几
long start=System.currentTimeMillis();
Long sum=0L;
ForkJoinPool forkJoinPool=new ForkJoinPool();
ForkJoinTask<Long> task=new ForkJoinDemo(0L,10_0000_0000L);
ForkJoinTask<Long> submit=forkJoinPool.submit(task);
sum=submit.get();
long end=System.currentTimeMillis();
System.out.println("sum="+sum+",时间:"+(end-start));
}
public static void test3(){//块几十倍
long start=System.currentTimeMillis();
Long sum=0L;
//Stream 并行流()
sum=LongStream.rangeClosed(0L,10_0000_0000L).parallel().reduce(0,Long::sum);
long end=System.currentTimeMillis();
System.out.println("sum="+sum+",时间:"+(end-start));
}
}
JMM
volatile是java虚拟机提供的轻量级的同步机制
1.保证可见性
2.不保证原子性
3.禁止指令重排
什么是jMM?
java内存模型,不存在的东西,概念!约定!
关于JMM的一些同步的约定:
1.线程解锁前,必须把共享变量立刻刷回主存
2.线程加锁前,必须读取主存中的最新值到工作内存中
3.加锁和解锁是同一把锁
线程 工作内存,主内存
8种操作
指令重排
什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行的.
源代码...->编译器优化重排...->指令并行也可能重排...->内存系统也会重排...->执行
int x=1; //1
int y=2; //2
x=x+5; //3
y=x*x; //4
volatile可以避免指令重排
内存屏障,cpu指令,作用
1.保证特定的操作的执行顺序
2.可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)
Volatile是可以保证可见性,不能保证原子性,由于内存屏障,可以避免指令重排
单例模式
各种锁的理解
公平锁和非公平锁
公平锁:非常公平,不能插入,必须按顺序执行
非公平锁:非常不公平,可以插队(默认是公平的)
可重入锁
自旋锁
是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环
public class SpinlockDemo {
//int 0
//Thread null
AtomicReference<Thread> atomicReference=new AtomicReference<>();
public void myLock(){
Thread thread=Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"==> mylock");
//自旋锁
while(!atomicReference.compareAndSet(null,thread)){
}
}
public void myUnLock(){
Thread thread=Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"==> myUnlock");
atomicReference.compareAndSet(thread,null);
}
}
测试:
public class TestSpinLock {
public static void main(String[] args) throws Exception{
ReentrantLock reentrantLock=new ReentrantLock();
reentrantLock.lock();
reentrantLock.unlock();
//底层使用自旋锁
SpinlockDemo lock=new SpinlockDemo();
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(3);
}catch (Exception e){
e.printStackTrace();
}finally {
lock.myUnLock();
}
},"T1").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(1);
}catch (Exception e){
e.printStackTrace();
}finally {
lock.myUnLock();
}
},"T2").start();
}
}
死锁
两个锁互相争抢
public class DeadLockDemo {
public static void main(String[] args) {
String lockA="lockA";
String lockB="lockB";
//A想获取B,B又想获取A,造成死锁
new Thread(new MyThread(lockA,lockB),"A").start();
new Thread(new MyThread(lockB,lockA),"B").start();
}
}
class MyThread implements Runnable{
private String lockA;
private String lockB;
public MyThread(String lockA,String lockB){
this.lockB=lockB;
this.lockA=lockA;
}
@Override
public void run(){
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get"+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"=>get"+lockA);
}
}
}
}
排查死锁:
使用Terminal 输入: jps -l
查看当前的进程
然后输入: jstack 进程号
可以看到是否有死锁,以及死锁在哪里产生
面试,工作中排查问题
1.日志
2.堆栈信息