多线程
synchronized
1、synchronized关键字
- 
简介 解决多个线程之间访问资源的同步性。保证被他修饰的方法或者是代码块在任意时刻只能有一个线程执行。 1.6 之前依赖底层的操作系统的来实现的,java线程映射到操作系统的原生线程之上的。原生切换线程,需要从用户态转成内核态要花费挺长的时间。 1.6之后,JVM层进行优化,所得效率也优化了很多。关于锁的实现也引入了大量的优化,降低了锁操作的开销。 
- 
如何使用?项目什么地方用 - 
三种使用方式: - 修饰实例方法,作用于当前对象实例,加锁,进入同步代码前要获得当前对象实例的锁 
- 修饰静态方法,作用域当前类对象加锁,计入同步代码前获得当前类对象的锁。 
 - 访问静态synchronized方法,占用的锁是类的锁,
- 访问非静态 synchronized方法占用个的锁是对象的锁,
- 这两个不会互斥
 - 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的
 
 
- 
2.原理
- 同步语句块的情况
    public class SynchronizedDemo{
        public void method(){
            synchronized(this){
                System.out.println("Ssynchroized 代码块");
            }
        }
    }
javac SynchronizedDemo.java
产生SynchronizedDemo.class
javap -c -s -v- l SynchronizedDemo.class
      public void method();
         descriptor: ()V
         flags: ACC_PUBLIC
         Code:
           stack=2, locals=3, args_size=1
              0: aload_0
              1: dup
              2: astore_1
              3: monitorenter
              4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
              7: ldc           #3                  // String Ssynchroized 代码块
              9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             12: aload_1
             13: monitorexit
             14: goto          22
             17: astore_2
             18: aload_1
             19: monitorexit
             20: aload_2
             21: athrow
             22: return
- 注意上边的monitorenter 这个就是为了获取monitor,本来计数器是0 然后获取后就会变成1,后续的继要获取的时候就会获取失败,直到另一个线程释放为止。
- 注意上边的monitorexit 释放线程,计数器重新变成0 然后就可以有后续的操作
- 
synchrronized修饰方法 public class SynchronizedDemo2{ public synchronized void method(){ System.out.println("synchronized 方法"); } }操作步骤同上 
      {
        public SynchronizedDemo2();
          descriptor: ()V
          flags: ACC_PUBLIC
          Code:
            stack=1, locals=1, args_size=1
               0: aload_0
               1: invokespecial #1                  // Method java/lang/Object."<init>":()V
               4: return
            LineNumberTable:
              line 1: 0
      
        public synchronized void method();
          descriptor: ()V
          flags: ACC_PUBLIC, ACC_SYNCHRONIZED
          Code:
            stack=2, locals=1, args_size=1
               0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
               3: ldc           #3                  // String synchronized 方法
               5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
               8: return
            LineNumberTable:
              line 3: 0
              line 4: 8
      }
      
见第15行,falges多个ACC_SUNCHRONIZED
3、概念
- 
对象的锁, - 
如果 Synchronized明确指定了锁对象,比如Synchronized(变量名)、 Synchronized(this)等,说明加解锁对象为该对象。
- 
如果没有明确指定: - 
Synchronized修饰的方法为非静态方法,表示此方法对应的对象为 锁对象
- 若 Synchronized修饰的方法为静态方法,则表示此方法对应的类对象 为锁对象。
 
- 
 
- 
- 
1.6的优化 - 自旋锁:把线程进行阻塞操作之前先让线程自旋等待一段时间
- 三种不同的Monitor实现:(也就是不同的Monitor)- 偏向锁:没有竞争出现时,默认会使用偏向锁。也就是在对象头上的MarkWord部分设置线程ID,标示这个对象偏向于当前线程,这个时候没有互斥
- 轻量级锁:如果另一个线程试图锁定被偏斜的对象,JVM就撤销偏斜锁,切换到轻量级锁实现。
- 重量级锁:轻量级锁依赖CAS操作MarkWord来试图获取锁,如果重试成功,
 就使用普通的轻量级锁;否则,进一步升级为重量级锁。
 
- 偏向锁:没有竞争出现时,默认会使用偏向锁。也就是在对象头上的
 
- 
Synchronized 非公平锁:  获取锁的行为上,并不是先到先得,而是任何一个线程都有机会领到锁,所以叫非公平锁。 
- 
锁消除和锁粗化 - 锁消除:指虚拟机即时编译器在运行时,对一些代码上要求同步,但被
 检测到不可能存在共享数据竞争的锁进行消除。
- 锁粗化:就是增大锁的作用域。原则上应该同步块范围尽量的小,但是如果对同一块频繁的由同一个对象频繁的加锁解锁,也是浪费资源。
 
- 锁消除:指虚拟机即时编译器在运行时,对一些代码上要求同步,但被
- 
悲观锁/乐观锁/ CAS- 悲观锁:并发策略是悲观的:不管是否会被竞争,所有的数据操作都要求被加锁,用户态核心态转换,维护锁计数器,检查是否有被阻塞线程需要被唤醒灯操作。
- 乐观锁:基于冲突检测的乐观并发策略。先进行操作,如没有其他线征用数据就没问题。如果产生了冲突,进行其他补偿措施,也叫做非阻塞同步。
- 乐观锁的核心算法是 CAS:内存值、预期值、新值。当且仅当预期值和内存值相
 等时才将内存值修改为新值。
 
- 
乐观锁的优劣 - 只能保证一个共享变量的原子操作
- 长时间自旋可能导致开销大
- 
ABA问题
 
- 
ReentrantLock可重入锁/Synchronized对比- 锁的实现原理基本是为了达到一个目的:让所有的线程都能看到某种标记。
- 
Synchronized:对象头中设置标记
- 
ReentrantLock/所有的基于 Lock 接口的实现类:用一个volitile修饰的int型变量,并保证每个线
 程都能拥有对该int的可见性和原子修改
- 
ReentrantLock有比synchorized多的功能- 等待可中断:长期不释放锁的时候,正在等待的线程可以选择放弃等待 lock.lockInterruptibly()
- 带超时的获取锁尝试:指定的时间范围内获取锁
- 可以判断是否有线程在排队等待获取锁。
- 可以响应中断请求:与 Synchronized不同,当获取到锁的线程被中 断时,能够响应中断,中断异常将会被抛出,同时锁会被释放。
- 可以实现公平锁。(ReentrantLock 既能够实现公平锁,也能够实现非公平锁,通过构造方里的参数来进行控制的)
- 在竞争不激烈时,Synchronized的性能要优于ReetrantLock;在高竞争情况下,Synchronized的性能会下降几十倍
 
- 等待可中断:长期不释放锁的时候,正在等待的线程可以选择放弃等待 
 
4、ReentrantLock使用
- 普通使用
package com.synchronizedDemo;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockTest extends Thread {
    public static ReentrantLock lock = new ReentrantLock();
    public static int i = 0;
    public ReentrantLockTest(String name) {
        super.setName(name);
    }
    @Override
    public void run() {
        for (int j = 0; j < 1000000; j++) {
            lock.lock();
            try {
                System.out.println(this.getName() + " " + i);
                i++;
            } finally {
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ReentrantLockTest test1 = new ReentrantLockTest("thread1");
        ReentrantLockTest test2 = new ReentrantLockTest("thread2");
        test1.start();
        test2.start();
        test1.join();
        test2.join();
        System.out.println(i);
    }
}
2、可中断
package com.synchronizedDemo;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.locks.ReentrantLock;
public class LockInterrupt extends Thread{
    public static ReentrantLock lock1 = new ReentrantLock();
    public static ReentrantLock lock2 = new ReentrantLock();
    int lock;
    public LockInterrupt(int lock, String name) {
        super(name);
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            if(lock ==1) {
                lock1.lockInterruptibly();
                Thread.sleep(500);
                lock2.lockInterruptibly();
            }else if (lock ==2) {
                lock2.lockInterruptibly();
                Thread.sleep(500);
                lock1.lockInterruptibly();
            }
            
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if(lock1.isHeldByCurrentThread()) {
                lock1.unlock();
            }
            if(lock2.isHeldByCurrentThread()) {
                lock2.unlock();
            }
            System.out.println(Thread.currentThread().getId() + ":线程退出");
        }
        
    }
    public static void main(String[] args) throws InterruptedException {
        LockInterrupt t1 = new LockInterrupt(1, "LockInterrupt1");
        LockInterrupt t2 = new LockInterrupt(2, "LockInterrupt2");
        t1.start();
        t2.start();
        Thread.sleep(1000);
        DeadlockChecker.check();
    }
    static class DeadlockChecker{
         private final static ThreadMXBean mbean = ManagementFactory
                    .getThreadMXBean();
         public static void check() {
             Thread tt = new Thread(()->{
                 while(true) {
                     long[] deadlockedThreadIds = mbean.findDeadlockedThreads();
                     if (deadlockedThreadIds != null) {
                         ThreadInfo[] threadInfos = mbean.getThreadInfo(deadlockedThreadIds);
                         for (Thread t : Thread.getAllStackTraces().keySet()) {
                             for (int i = 0; i < threadInfos.length; i++) {
                                 if (t.getId() == threadInfos[i].getThreadId()) {
                                     System.out.println(t.getName());
                                     t.interrupt();//中断
                                 }
                             }
                         }
                     }
                 }
             });
             tt.setDaemon(true);
             tt.start();
         }
    }
}
- 锁定的时候用lock1.lockInterruptibly();
- 中断时候,找到这个线程,然后直接  t.interrupt();
3、超时中断(代码太多,和上边比较类似,就是锁定的时候显示)
lock.tryLock(5, TimeUnit.SECONDS)
4、公平锁,初始化锁的时候,
public static ReentrantLock fairLock = new ReentrantLock(true);
5、JUC/AQS
- 
AQS框架- 一个用来构建锁和同步器的框架
- 内部定义了一个 volatile int state变量,表示同步状态;state =0的时候表示没有线程占用,可以获得锁,同时将state=1.如过state=1表示其他锁正在用,加入同步队列
- 通过 Node 内部类构成的一个双向链表结构的同步队列