Synchronized锁的只能是引用类型不能是基本类型
锁的时间:当代码块执行完成之后,锁自动释放
锁的定义:对资源进行一个标记,当此资源变为锁住的状态时,其他线程就无法访问这个资源
当线程调度到其他时间片上时,这个锁的状态不会丢,其他线程依旧无法访问这个资源
线程的执行顺序是随机的,放在第一个的有很大概率第一个执行,但不一定
问题:当两个线程之间隔着一些普通功能的代码,那么排在前面的线程与排在后面的线程的执行顺序也还是随机的吗,换而言之,是不是线程的代码的优先级大于非线程的优先级,t1.start()>普通功能代码
个人答案:主线程肯定是最先执行的,因此并不是所有的线程同步执行
举一个死锁的例子
答案:
public class DeadLockDemo {
private static String A = "A";
private static String B = "B";
public static void main(String[] args) {
new Test().deadLock();
}
private void deadLock() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (A) {
try {
Thread.currentThread().sleep(2000);
}catch (InterruptedException e) { e.printStackTrace();
}
synchronized (B) { System.out.println("1"); } } } });
Thread t2 = new Thread(new Runnable() {
@Override
public void run() { synchronized (B) {
synchronized (A) {
System.out.println("2"); } } } });
t1.start();
t2.start();
}
}
补充:先执行t2可能不会出现死锁
如何避免死锁
-
避免一个线程同时获取多个锁
-
避免一个线程再锁内同时占用多个资源,尽量vaoz每个锁只占一个资源
-
尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁
-
对于数据库锁,加锁和解锁必须在同一个数据库的连接里否则会出现锁失败的情况
线程的可见性:每个线程在对数据进行修改时,都有一个副本数据,在多线程的情况下,一个线程修改了变量,其他使用这个变量的线程可以立即获得这个修改之后的数据
volatile比synchronized使用和执行成本更低,不会增加调度和上下文切换,被他修饰的数据,可读不可写,例如,t1,t2线程对同一个资源进行了获取,t1使用了volatile,t1对数据进行了修改,t2则读取的数据无效,需要重新读取
内存屏障:是一组处理器指令,用于实现对内存操作的顺序限制,即通过一些规则,使内存操作有序
缓冲行:缓存中可以分配的最小的存储单位
原子操作:要么都成功,有不成功的则回滚到执行前
缓存行的填充:单个数据占满整个缓冲行,总线不会被阻塞,可以更快的被读取
缓存:在内存和磁盘之间存在一些存储空间,在里面存放一些常用的地址,当需要查找时,先从这里面中获得,没有再从磁盘中查找,缓存的数据有滞后性
缓存命中:在缓存中读取到想要的数据
写命中:当处理器将操作数写回到一个内存缓存的区域时,他首先会检查这个缓存的内存地址是否在缓存行中,如果存在一个有效的缓存行,则处理器将这个操作数写回到缓存
写缺少:一个有效的缓存行写入到不存在的内存区域,举例,当一个线程将那块内存区域标志为无效,而另一个线程则从缓存中获取数据往那块区域写
缓存往外部写的时候,一定是先传地址再传数据
锁总线的意思是,一个进程的全部指令执行完成之后才能进行其他进程的指令
锁缓存:就是给缓存加锁,当有数据经总线往外写时,其他缓存不允许进行修改
Java中的每一个对象都可以作为锁,具体表现形式为:
-
对于普通方法,锁是当前实例对象
-
对于静态方法,锁是当前类的Class对象
-
对于同步代码块,锁是Synchronized括号里配置的对象
Synchronized用的锁是存在Java对象头里的,数组类型用三个字宽存储对象头,非数组则用两个字宽存储对象头。
锁一共有4种状态,级别从低到高依次是:无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态
锁标志位00表示轻量级锁,10表示重量级锁,11表示GC标记,要被回收利用,01即表示偏向锁,也表示无锁,区分方法是偏向锁位,1是偏向锁,0表示无锁
锁可以升级但是不可以降级,目的是为了提高获得锁和释放锁的效率
锁 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
偏向锁 | 加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级的差距 | 如果线程存在竞争,会带来额外的锁撤销的消耗 | 只有一个线程访问同步块场景 |
轻量级锁 | 竞争的线程不会阻塞,提高了程序的相应速度 | 如果始终得不到锁竞争的线程,使用自旋会消耗CPU | 追求响应时间,同步块执行速度非常快 |
重量级锁 | 线程竞争不使用自旋,不会消耗CPU | 线程阻塞,响应时间缓慢 | 追求吞吐量,同步块执行速度较长 |