1. 并发编程遇到的挑战
多线程 减少上下文切换的方式:
- 无锁并发编程:多线程竞争锁的时候会引起上下文的切换,在多线程处理数据的时候可以使用一些方法来避免使用锁;如将id按照hash算法取模分段,不同的线程处理不同段的数据
- cas算法,
- 使用最少的线程。避免创建太多不必要的线程,可以减少上下文切换
- 协成:使用单线程实现多任务的调度,在单线程中维持多个任务间的切换
避免多个线程出现死锁
- 避免一个线程同时获取多个锁
- 避免一个线程在锁内同时占用多个锁,尽量保证一个锁占用一个资源
- 尝试使用定时锁,使用lock.tryLock(timeout) 来代替使用内部锁机制
- 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败
public class DeadLockDemo {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
new DeadLockDemo().deadlock();
}
private void deadlock() {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("thread 1 running");
}
}
}
}, "thread==>1");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("thread 2 running");
}
}
}
}, "thread==>2");
thread1.start();
thread2.start();
}
}
2. Java并发机制的底层实现原理
Java CAS的原理:
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当旧的预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。这其实就是一个乐观锁
synchronized 同步表现形式:
- 对于普通的同步方法,锁是当前实例对象
- 对于静态同步方法,锁是当前类的class对象
- 对于同步方法块,锁是synchronized括号里面的对象
锁的类型有四种状态,由低到高:无锁状态,偏向锁,轻量级锁,重量级锁。锁的状态可以升级,但是不能降级
参考:https://www.cnblogs.com/paddix/p/5405678.html
状态之间的转换
3. Java 内存模型
Java内存模型的抽象结构
volatile 的特性
可见性:对于一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入
原子性:对任意单个volatile变量的读、写具有原子性,但是类似于volatile++这种复合操作不具备原子性
volatile 的写-读的内存语义
-
volatile写的内存语义:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。
volatile读内存的语义:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量
锁的释放和获取的内存语义
-
当线程释放锁的时候,JMM会把线程对应的本地内存中的共享变量刷新到主内存中。
-
当线程获取锁的时候,JMM会把该线程对应的本地内存置为无效。