1. JVM 同步基于进入和退出,使用 管程 对象 (monitor 监视器)实现
  2. 进程 1.
  3. 线程
    1. 进程里的最小执行单元
    2. 用户线程:new Thread()
    3. 守护线程:jvm垃圾回收
  4. 多线程:共享资源,资源争夺
  5. 创建线程
    1. 继承 Thread.class
    2. 实现 Runnable
    3. 使用 Callable -call()
    4. 使用线程池
//  当前正在执行的线程 睡眠 
Thread.sleep();
// 当前正在执行的线程 谦让的退出 ---> 回到等待队列
Thread.yield();  

 public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                for (int i = 0; i < 10; i++) {
                    System.out.println(i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });
        t1.start();

        Thread t2 = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    if (i == 3) {
//                        t2 线程正在执行中 ,t1 join 优先加入执行完毕后,t2 才可以继续执行
                        t1.join();
                    }
                    System.out.println("t2=======>"+i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });
        t2.start();


    }
// 如果在 主线程中调用 ,可以保证 线程执行完毕后, 有序结束
     t1.join();
        t2.join();
        System.out.println(" main .....end ");

synchronized

  • 既保证了原子性 又保证了可见性
  • 可重入锁
     public synchronized static void testLock() {
//        synchronized == synchronized (Ticket.class){}
    }

    public synchronized void outket() {
//          public synchronized void outket()  ===      synchronized (this){}
    }




   /* 锁 this  独占 */ 
public class Ticket {
     
    Integer money;

    public synchronized void add() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.money = 100;
    }

    public synchronized Integer get() {
        return this.money;
    }

    public static void main(String[] args) {
        Ticket t = new Ticket();
        new Thread(t::add).start();
        try {
            TimeUnit.MILLISECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(t.get());
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(t.get());

    }

}


// 可重入锁 

    synchronized  void a(){
        b();
    }
    synchronized  void b(){}
  • 程序在执行过程中,如果出现异常,默认情况锁会被释放
  • 所以,在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况。
  • 比如,在一个web app处理过程中,多个servlet线程共同访问同一个资源,这时如果异常处理不合适,
  • 在第一个线程中抛出异常,其他线程就会进入同步代码区,有可能会访问到异常产生时的数据。
  • 因此要非常小心的处理同步业务逻辑中的异常

 


synchronized的底层实现
JDK早期的 重量级 - OS
后来的改进
锁升级的概念:
        sync (Object)
        markword 记录这个线程ID (偏向锁)
        如果线程争用:升级为 自旋锁
        10次以后,
        升级为重量级锁 - OS

        执行时间短(加锁代码),线程数少,用自旋
        执行时间长,线程数多,用系统锁

适用

  1. 执行时间短 (自旋锁 等待消耗资源)
  2. 线程数量少 (1个线程上锁, 9999999 自旋等待)

lock

  • 手动上锁,解锁 lock unlock
  1. 读写锁
  2. 可重入锁