您的位置:首页 > 文旅 > 美景 > 视觉设计部是干什么的_杭州人防质监站网址_万能浏览器_win7优化大师好不好

视觉设计部是干什么的_杭州人防质监站网址_万能浏览器_win7优化大师好不好

2024/12/23 11:27:52 来源:https://blog.csdn.net/qq_46248151/article/details/144090131  浏览:    关键词:视觉设计部是干什么的_杭州人防质监站网址_万能浏览器_win7优化大师好不好
视觉设计部是干什么的_杭州人防质监站网址_万能浏览器_win7优化大师好不好

volatile

volatile 是 JVM 提供的 最轻量级的同步机制,中文意思是不稳定的,易变的,用 volatile 修饰变量是为了保证变量在多线程中的可见性,它表达的含义是:告诉编译器,对这个变量的读写,需要基于主内存保证多CPU的缓存一致性。

volatile 变量的两个特性:解决可见性和有序性

1、解决可见性

保证变量对所有线程的可见性:当一条线程修改了 volatile 变量的值,新值对于其他线程来说是可以立即得知的。而普通变量不能做到这一点

线程写 volatile 变量的过程:

  1. 改变线程工作内存中 volatile 变量副本的值
  2. 将改变后的副本的值立即从工作内存刷新到主内存

线程读 volatile 变量的过程:

  1. 从主内存中读取 volatile 变量的最新值到线程的工作内存中
  2. 从工作内存中读取 volatile 变量的副本

注意:

1、volatile并不能保证并发操作的原子性,即不保证线程安全

2、volatile修饰引用类型,它只能保证引用本身的可见性,不能保证所引用对象内部属性的可见性

2、解决有序性

禁止进行指令重排序,具体一点解释,禁止重排序的规则如下:

  • volatile变量时,可以确保volatile写之前的操作不会被编译器重排序到volatile写之后。
  • volatile变量时,可以确保volatile读之后的操作不会被编译器重排序到volatile读之前。

synchronized

如果某一个资源被多个线程共享,为了避免因为资源抢占导致资源数据错乱,我们需要对线程进行同步,那么synchronized就是实现线程同步的关键字

synchronized的作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果。

其用法如下:

从语法上讲,Synchronized可以把任何一个非null对象作为"锁",在HotSpot JVM实现中,锁有个专门的名字:对象监视器(Object Monitor)

// (1)修饰静态方法
public synchronized static void helloStatic(){System.out.println("hello world static");
}// (2)修饰成员函数
public synchronized void hello(){System.out.println("hello world");
}// (3)直接定义代码块
public void test(){SynchronizedTest test = new SynchronizedTest();        synchronized (test){System.out.println("hello world");}
}

synchronized其底层说白了就是锁,所以呢,我们先从锁讲起

我们知道发生原子性的根源是CPU在执行完任意指令后都有可能发生线程切换。如果能够禁用线程切换的话那这个问题也就迎刃而解了。操作系统做线程切换是依赖 CPU 中断的,所以禁止 CPU 发生中断就能够禁止线程切换。

1、加锁和解锁操作在哪里体现的?

synchronized 的加锁和解锁是隐式实现的,可以查看字节码

2、synchronized 的锁对象是什么,也就是说锁定的是哪个对象?

  • 如果修饰的是代码块,锁对象是我们自己指定的,指定哪个对象就锁定哪个对象。
  • 如果修饰的是非静态方法,锁定的是当前实例对象 this
  • 如果修饰的是静态方法,锁定的是当前类的 Class 对象。

Linux内核提供的锁机制

1、Mutex(互斥量):pthread_mutex_t,通过对该结构的操作,来判断资源是否可以访问,Mutex属于sleep-waiting类型的锁,例如在多核机器上有两个线程A,B,如果此时锁被A持有,那么B就会被阻塞,在等待队列中等待。

2、Spin lock(自旋锁):pthread_spinlock_t,属于busy-waiting类型的锁,它不会引起调用者睡眠等待,如果获取不到锁则进入忙等待,它会不停的尝试去获取锁,俗称自旋,获取锁的性能相对较高,但是费CPU,所以自旋锁不应该被长时间的持有。

3、 Condition Variable(条件变量):pthread_cond_t,条件变量是利用线程间共享的全局变量,进行同步的一种机制

4、Read/Write Lock(读写锁):pthread_rwlock_t,读写锁是用来解决读多写少问题的,读操作可以共享,写操作是排他的。

5、semaphore:sem_t,信号量(semaphore)机制,也可用于互斥锁的实现

synchronized底层

public static void synClass() {Object obj = new Object();synchronized (obj) {}
}

我们看看这段代码的字节码文件

 0 new #2 <java/lang/Object>3 dup4 invokespecial #1 <java/lang/Object.<init> : ()V>7 astore_18 aload_19 dup
10 astore_2
11 monitorenter
12 aload_2
13 monitorexit
14 goto 22 (+8)
17 astore_3
18 aload_2
19 monitorexit
20 aload_3
21 athrow
22 return

其中跟synchronized关键字相关的就是这样的字节码

monitorenter
........
monitorexit

monitorenter主要是获取监视器锁,monitorexit主要是释放监视器锁,所以我们的synchronized就是去获取这个监控锁对象,他会在我们的对象头中(markword)

markword图示如下:

加锁过程

前面我们看到了synchronized在字节码层面是对应monitorentermonitorexit,而真正实现互斥的锁其实依赖操作系统底层的Mutex Lock来实现,首先要明确一点,这个锁是一个重量级的锁

整体锁升级的过程大致可以分为两条路径,如下:

1、偏向锁未启动,加锁默认加轻量级锁

轻量级锁:线程在自己的线程栈生成Lock Record,使用CAS的方式将markword设置为指向自己线程LOCK Record的指针,设置成功者得锁。竞争会让锁膨胀为重量级锁。

2、偏向锁启动

偏向锁,偏向的是第一个来获取锁的线程。所谓偏向锁,指的是获取锁的线程在markword中写自己的线程ID的过程,偏向锁升级为轻量级锁时首先要撤销偏向锁,如何设置轻量级锁。

偏向锁默认是打开的,但是启动有一个时延,默认4s,之所以要延迟,是因为JVM虚拟机自己有一些默认的启动线程,里面有好多sync代码,这些代码启动时就肯定会有竞争,如果直接使用偏向锁,就会造成偏向锁不断的进行锁撤销和锁升级的操作,效率较低。

我们平时使用synchronized注意点总结如下:

  • 降低锁的等级

    能用对象级别的,尽量别用类锁,能用实例变量的不要用静态变量

  • 减少锁的时间 不需要同步执行的代码,能不放在同步块里面执行就不要放在同步快内,可以让锁尽快释放

  • 减少锁的粒度 共享资源数决定锁的数量。有一组资源定义一把锁,而不是多组资源共用一把锁,增加并行度,从而降低锁竞争,典型如分段锁

  • 减少加减锁的次数 假如有一个循环,循环内的操作需要加锁,我们应该把锁放到循环外面,否则每次进出循环,都要加锁

  • 读写锁 业务细分,读操作加读锁,可以并发读,写操作使用写锁

  • 善用volatile

    volatile的控制比synchronized更轻量化,在某些变量上不涉及多步打包操作和原子性问题,可以加以运用。

    如ConcurrentHashMap的get操作,使用的volatile而不是加锁

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com