Java并发探索(一)

129 阅读2分钟

「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

Java并发

上下文切换

时间片是CPU分配给各个线程的时间,时间片非常短,CPU通过不停的切换线程执行,感觉多个线程是同时执行的。

任务从保存到再加载的过程就是一次上下文切换

构造方法不能synchronized修饰,构造方法本身就是线程安全的。

为什么会存在并发执行比串行执行的速度要慢的情况?

因为线程有创建和上下文切换的开销。

如何减少上下文切换

无锁并发编程、CAS算法、最少线程、使用协程

  • 无锁并发编程

多线程竞争锁会引起上下文切换。避免使用锁的解决方案,如将数据的ID按照Hash算法取模分段,不同的线程处理不同的数据。

  • CAS算法

Java的Atomic包使用CAS算法来更新数据,不需要加锁。

  • 最少线程

避免创建不需要的线程。

  • 使用协程

在单线程里实现多任务的调度,以及多任务的切换。

引起死锁的条件

如何避免死锁

  • 保证一个线程在同一时间只能获取一个锁。
  • 避免一个线程在锁内同时占有多个资源,保证每个锁只占用一个资源。
  • 尝试使用定时锁 lock.tryLock(timeout)
  • 数据库锁加锁和解锁必须在一个数据库连接里。

synchronized底层原理剖析:

javac SynchronizedDemo.java

javap -c -s -v -l SynchronizedDemo.class

修饰代码块:

public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            System.out.println("synchronized 代码块");
        }
    }
}

image-20210622120058457.png 同步代码块是monitorentermonitorexit指令实现的。

monitorenter:会尝试获取对象的锁,锁的计数器为0时,表示锁可以被获取,拿到锁后,计数器+1

monitorexit:将锁的计数器设为0,锁被释放。

修饰方法:

public class SynchronizedDemo2 {
    public synchronized void method() {
        System.out.println("synchronized 方法");
    }
}

image-20210622115254841的副本2.png

ACC_SYNCHRONIZED:表示该方法为同步方法。JVM 通过 ACC_SYNCHRONIZED 标识来判断一个方法是否为同步方法。

monitorentermonitorexit指令 与 ACC_SYNCHRONIZED的本质都对monitor监视器的获取。