一、synchronized 加锁方法与原理
synchronized是一种互斥锁
- 一次只能允许一个线程进入被锁住的代码块
synchronized是一种内置锁/监视器锁
- Java中每个对象都有一个内置锁(监视器,也可以理解成锁标记),而synchronized就是使用**对象的内置锁(监视器)**来将代码块(方法)锁定的
synchronized的好处
- synchronized保证了线程的原子性。(被保护的代码块是一次被执行的,没有任何线程会同时访问)
- synchronized还保证了可见性。(当执行完synchronized之后,修改后的变量对其他的线程是可见的)
synchronized的缺点
synchronized加锁公式
//修饰方法
public synchronized void test1(){
}
//修饰代码块
synchronized (任意对象){
}
- 普通同步方法,锁是当前实例对象
- 静态同步方法,锁是当前类的class对象
- 同步方法块,锁是括号里面的对象
特性
重入锁
public class Widget { // 锁住了
public synchronized void doSomething() { ... }
}
public class LoggingWidget extends Widget { // 锁住了
public synchronized void doSomething() {
System.out.println(toString() + ": calling doSomething");
super.doSomething();
}
}
- 当线程A进入到LoggingWidget的
doSomething()
方法时,此时拿到了LoggingWidget实例对象的锁。 - 随后在方法上又调用了父类Widget的
doSomething()
方法,它又是被synchronized修饰。 - 那现在我们LoggingWidget实例对象的锁还没有释放,进入父类Widget的
doSomething()
方法还需要一把锁吗?
不需要的!
因为锁的持有者是“线程”,而不是“调用”。线程A已经是有了LoggingWidget实例对象的锁了,当再需要的时候可以继续**“开锁”**进去的!
这就是内置锁的可重入性。
二、Lock接口
- Lock方式来获取锁支持中断、超时不获取、是非阻塞的
- 提高了语义化,哪里加锁,哪里解锁都得写出来
- Lock显式锁可以给我们带来很好的灵活性,但同时我们必须手动释放锁
- 支持Condition条件对象
- 允许多个读线程同时访问共享资源
lock.lock();
if (court > 0) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "第" + court--);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}
三、volatile关键字
内存一致性
Java中的volatile可以看做是“轻量级的synchronized”。synchronized可能会引起上下文切换和线程调度,同时保证可见性、有序性和原子性。volatile不会引起上下文切换和线程调度,但仅提供可见性和有序性保证,不提供原子性保证。
volatile关键字的作用很简单,就是一个线程在对主内存的某一份数据进行更改时,改完之后会立刻刷新到主内存。并且会强制让缓存了该变量的线程中的数据清空,必须从主内存重新读取最新数据。
防止指令重排序
为了增加执行速度,一般执行程序时是乱序执行的。
volatile为了执行准确性,不能进行乱序执行
使用方式
class A{
int a;
//volatile关键字直接加到字段上即可保证这个字段有上面两个功能
volatile int b;
}
四、JUC原子类
JUC原子类位于java.util.concurrent.atomic包下 ,主要实现的是Unsafe类
1. CAS
出现ABA问题,加版本号,验证版本号
2.atomic包
可以将包中的类分为五类:
- 基本类型:AtomicBoolean、AtomicInteger、AtomicLong
- 引用类型:AtomicReference、AtomicStampedRerence、AtomicMarkableReference
- 数组:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
- 对象的属性:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
- JDK1.8新增:DoubleAccumulator、LongAccumulator、DoubleAdder、LongAdder
使用
public class AtomicTest {
public static void main(String[] args) throws InterruptedException {
myAtomic myAtomic = new myAtomic();
Thread thread1 = new Thread(){
@Override
public void run() {
for (int i = 0 ; i<1000;i++){
myAtomic.add(1);
}
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
for (int i = 0 ; i<1000;i++){
myAtomic.add(1);
}
}
};
thread1.start();
thread2.start();
//thread1.join();主线程执行到这必须把那俩子线程执行完才继续的
//thread2.join();
Thread.sleep(2000);
System.out.println(myAtomic.get());
}
}
class myAtomic{
private AtomicInteger value = new AtomicInteger(0);
public int add(int m){
return value.addAndGet(m);
}
public int dec(int m){
return value.addAndGet(-m);
}
public int get(){
return value.get();
}
}
五、锁的分类
前两行是MarkWord:记录锁信息、hashcode、GC
重量级与轻量级:是否需要操作系统处理的是重量级,反之轻量级
补充:
使用锁升级来实现
偏向锁—>自旋锁/轻量级锁–>重量级锁(锁升级、锁膨胀)
六、死锁
发生条件
- 当前线程拥有其他线程需要的资源
- 当前线程等待其他线程已拥有的资源
- 都不放弃自己拥有的资源
public static void main(String[] args) {
LockTest lockTest = new LockTest();
Thread thread1 = new Thread(lockTest);
thread1.start();
Thread thread2 = new Thread(lockTest);
thread2.start();
Thread thread3 = new Thread(lockTest);
thread3.start();
}
public class LockTest implements Runnable {
private static int court = 100;
private Object object = new Object();
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
synchronized (object) {
if (court > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "第" + court--);
}
}
}
}
}
七、一次错误的尝试
public static void main(String[] args) {
LockTest lockTest = new LockTest();
Thread thread1 = new Thread(new LockTest());
thread1.start();
Thread thread2 = new Thread(new LockTest());
thread2.start();
Thread thread3 = new Thread(new LockTest());
thread3.start();
}
public class LockTest implements Runnable {
private static int court = 100;
private Object object = new Object();
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
synchronized (object) {
if (court > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "第" + court--);
}
}
}
}
}