JUC学习笔记

JUC

什么是JUC

java.util 工具包、包、分类

业务:普通的线程代码 Thread

Runnable 没有返回值。效率相比Callable较低

线程和进程

进程

程序的集合

计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位

一个进程往往可以包含多个线程,至少一个

线程

是操作系统能够进行运算调度的最小单位

java默认有两个线程(main、GC(垃圾回收))

对于java:Thread、Runnable、Callable

java不可以开启线程

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
//本地方法 调用的底层C++,java无法直接操作硬件
    private native void start0();

并发、并行

并发编程:并发、并行

并发:多线程操作同一个资源

  • CPU一核,快速交替线程

并行:多个人一起行走

  • CPU多核,多个线程可以同时执行;线程池
public static void main(String[] args) {
        //获取CPU核数
        //CPU密集型 IO密集型
        System.out.println(Runtime.getRuntime().availableProcessors());
    }

并发编程的本质:充分利用CPU的资源

线程的状态

public enum State {
        // 新生
        NEW,

        // 阻塞
        RUNNABLE,

        // 等待,死死地等
        WAITING,

        // 超时等待
        TIMED_WAITING,

        // 终止
        TERMINATED;
    }

wait、sleep区别

  1. 来自不同的类

    wait–Object

    sleep–Thread

  2. 关于锁的释放

    wait会释放锁,sleep睡觉了,抱着锁睡觉,不会释放

  3. 使用的范围不同

    wait必须在同步代码块中

    sleep可以在任何地方

  4. 是否需要捕获异常

    wait不需要捕获异常

    sleep必须要捕获异常

Lock锁(重点)

传统synchronize

public class Test01 {
    public static void main(String[] args) {
        //并发:多线程操作同一个资源类,把资源类丢入线程
        Ticket ticket = new Ticket();
        //@FunctionalInterface函数式接口 简化匿名内部类 lambda表达式
        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}
//资源类 OOP
class Ticket{
    //属性、方法
    private int number = 50;
    //synchronize 本质:队列 锁
    public synchronized void sale(){
        if (number>0){
            System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+",剩余"+number);
        }
    }
}

Lock接口

公平锁:十分公平,先来后到

非公平锁(默认):十分不公平,可以插队

public class Test02 {
    public static void main(String[] args) {
        //并发:多线程操作同一个资源类,把资源类丢入线程
        Ticket1 ticket1 = new Ticket1();
        //@FunctionalInterface函数式接口 简化匿名内部类 lambda表达式
        new Thread(() -> { for (int i = 0; i < 50; i++) ticket1.sale(); }, "A").start();
        new Thread(() -> { for (int i = 0; i < 50; i++) ticket1.sale(); }, "B").start();
        new Thread(() -> { for (int i = 0; i < 50; i++) ticket1.sale(); }, "C").start();
    }
}
//资源类 OOP
class Ticket1 {
    //属性、方法
    private int number = 50;
    Lock lock = new ReentrantLock();

    public void sale() {
        lock.lock();
        try {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + ",剩余" + number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Synchronize和Lock区别

  1. Synchronize 内置的java关键字;Lock是一个java类
  2. Synchronize 无法判断获取锁的状态;Lock可以手动判断是否获取到了锁
  3. Synchronize 会自动释放锁;Lock必须要手动释放锁,如果不释放,死锁
  4. Synchronize 线程1获得锁后阻塞,线程2一直等待;Lock锁就不一定会等待下去
  5. Synchronize 可重入锁,不可以中断,非公平;Lock,可重入锁,可以判断锁,可以自己设置公平非公平
  6. Synchronize 适合锁少量的代码同步问题;Lock适合锁大量的同步代码

生产者和消费者问题

生产者和消费者问题synchronize版

public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
class Data{
    private int number = 0;
    public synchronized void increment() throws InterruptedException {
        if (number!=0)
            this.wait();//等待
        number++;
        System.out.println(Thread.currentThread().getName()+"--"+number);
        this.notifyAll();//通知
    }
    public synchronized void decrement() throws InterruptedException {
        if (number==0)
            this.wait();
        number--;
        System.out.println(Thread.currentThread().getName()+"--"+number);
        this.notifyAll();
    }
}

存在的问题 多个线程存在时

if改为while判断

public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class Data{
    private int number = 0;
    public synchronized void increment() throws InterruptedException {
        while (number!=0)
            this.wait();//等待
        number++;
        System.out.println(Thread.currentThread().getName()+"--"+number);
        this.notifyAll();//通知
    }
    public synchronized void decrement() throws InterruptedException {
        while (number==0)
            this.wait();
        number--;
        System.out.println(Thread.currentThread().getName()+"--"+number);
        this.notifyAll();
    }
}

生产者消费者JUC版

代码实现:

public class B {
    public static void main(String[] args) {
        Data1 data = new Data1();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class Data1{
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
//condition.await();//等待
//condition.signalAll();//唤醒全部
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (number!=0)
                condition.await();
            number++;
            System.out.println(Thread.currentThread().getName()+"--"+number);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (number==0)
                condition.await();
            number--;
            System.out.println(Thread.currentThread().getName()+"--"+number);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Condition 精准通知和唤醒线程

监视器实现顺序执行

public class C {
    public static void main(String[] args) {
        Data2 data2 = new Data2();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data2.a();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data2.b();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data2.c();
            }
        },"C").start();
    }
}
class Data2{
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();
    public void a(){
        lock.lock();
        try {
            while (number!=0)
                condition1.await();//等待
            System.out.println(Thread.currentThread().getName()+"aaa");
            number=1;//唤醒指定
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void b(){
        lock.lock();
        try {
            while (number!=1)
                condition2.await();
            System.out.println(Thread.currentThread().getName()+"bbb");
            number=2;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void c(){
        lock.lock();
        try {
            while (number!=2)
                condition3.await();
            System.out.println(Thread.currentThread().getName()+"ccc");
            number=0;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

8锁现象

/**
 * 8锁
 *
 * 1. 标准情况下 两个线程先发短信 延迟一秒后打电话
 * 2. 发的短信延迟四秒进行 两个线程先发短信 延迟四秒后打电话
 */
public class A {
    public static void main(String[] args) {
        Phone phone = new Phone();

        //锁的存在
        new Thread(()->{
            phone.sendMessage();
        }).start();

        //延迟一秒 捕获异常
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        }).start();
    }
}
class Phone{
    //synchronize 锁的对象是方法的调用者
    //两个方法是用的同一个锁 谁先拿到谁先执行
    public synchronized void sendMessage(){
        try {
            //sleep不会释放锁
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendMessage...");
    }
    public synchronized void call(){
        System.out.println("call...");
    }
}

/**
 * 3. 增加了普通方法 先执行普通方法(必须得有延迟)
 * 4. 两个对象 两个同步方法 先执行快的call
 */
public class B {
    public static void main(String[] args) {
        Phone1 phone = new Phone1();
        Phone1 phone2 = new Phone1();

        //锁的存在
        new Thread(()->{
            phone.sendMessage();
        }).start();

        //延迟一秒 捕获异常
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        }).start();
        new Thread(()->{
            phone.hello();
        }).start();
    }
}
class Phone1{
    //synchronize 锁的对象是方法的调用者
    //两个方法是用的同一个锁 谁先拿到谁先执行
    public synchronized void sendMessage(){
        try {
            //sleep不会释放锁
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendMessage...");
    }
    public synchronized void call(){
        System.out.println("call...");
    }
    //这里没有锁 不是同步方法 不收锁的影像
    public void hello(){
        System.out.println("hello");
    }
}
/**
 * 5. 增加两个静态方法,一个对象
 * 6. 两个对象
 */
public class C{
    public static void main(String[] args) {
        //两个对象的class模板只有一个 static 锁的是class对象
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        //锁的存在
        new Thread(()->{
            phone1.sendMessage();
        }).start();

        //延迟一秒 捕获异常
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        }).start();
    }
}
//存在唯一的class对象
class Phone2{
    //synchronize 锁的对象是方法的调用者
    //static 静态方法
    //类一加载就有了,锁的是class对象
    public static synchronized void sendMessage(){
        try {
            //sleep不会释放锁
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendMessage...");
    }
    public static synchronized void call(){
        System.out.println("call...");
    }
}
/**
 * 7. 一个静态 一个普通同步方法 一个对象 锁的对象不是一个 一个是class对象 一个是类对象
 * 8. 一个静态 一个普通同步 两个对象
 */
public class D {
    public static void main(String[] args) {
        //两个对象的class模板只有一个 static 锁的是class对象
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();

        //锁的存在
        new Thread(()->{
            phone1.sendMessage();
        }).start();

        //延迟一秒 捕获异常
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        }).start();
    }
}
//存在唯一的class对象
class Phone3{
    //synchronize 锁的对象是方法的调用者
    //static 静态方法
    //类一加载就有了,锁的是class对象
    public static synchronized void sendMessage(){
        try {
            //sleep不会释放锁
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendMessage...");
    }
    public  synchronized void call(){
        System.out.println("call...");
    }
}

new 锁的具体的一个对象

static 锁的Class唯一的模板

集合类不安全

list

//java.util.ConcurrentModificationException 并发修改异常
public class ListTest01 {
    //并发下ArrayList并不安全

    /**
     *解决方案
     * 1.List<String> list = new Vector<>();
     * 2.List<String> list = Collections.synchronizedList(new ArrayList<>());
     * 3.List<String> list = new CopyOnWriteArrayList<>();
     */
    //CopyOnWrite 写入时复制 COW 计算机设计领域的一种优化策略
    //多个线程在调用的时候 list读取的时候是固定的 写入的时候会有覆盖问题
    //在写入的时候避免覆盖问题,造成数据影响
    //CopyOnWrite比Vector效率高 Vector使用了synchronize CopyOnWrite使用了lock锁
    public static void main(String[] args) {
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

set

//ConcurrentModificationException
public class SetTest01 {
    public static void main(String[] args) {
        //Set<String> set = new HashSet<>();
        //Set<String> set = Collections.synchronizedSet(new HashSet<>());
        Set<String> set = new  CopyOnWriteArraySet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

hashset底层

public HashSet() {
        map = new HashMap<>();
    }
    //add
   public boolean add(E e) {
        return map.put(e, PRESENT)==null;
       
       
       private static final Object PRESENT = new Object();//恒定不变的值

map

//ConcurrentModificationException
public class MapTest01 {
    public static void main(String[] args) {
//        Map<String, String> map = new HashMap<>(); // 实际中不使用HashMap 默认等价于new HashMap<>(16,0.75);
        //Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

Callable接口

和Runnable的区别

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同 run()/call()

代码测试

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread myThread = new MyThread();
        FutureTask futureTask = new FutureTask(myThread);//适配类
        new Thread(futureTask,"A").start();
        Integer o = (Integer)futureTask.get();//获取Callable的返回结果
        System.out.println(o);
    }
}
class MyThread implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        return 1024;
    }
}

如果有两条线程 只会输出一个1024:由于JVM第二次调用FutrueTask对象所持有的线程,此时FutrueTask的state已经非new状态,此时会直接结束对应线程,导致任务也不执行,只是在第一次调用时返回结果保存了

细节:

  1. 有缓存
  2. 结果可能需要等待,会阻塞

常用的辅助类

1、CountDownLatch

减法计数器

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);//总数为6
        for (int i = 0; i < 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"Go Out");
                countDownLatch.countDown();//数量-1
            },String.valueOf(i)).start();
        }
        countDownLatch.await();//等待计数器为0,然后再向下执行
        System.out.println("Close");
    }
}

原理:

countDownLatch.countDown();//数量-1

countDownLatch.await();//等待计数器为0,然后再向下执行

每次有线程调用countDown()数量-1,假设计数器变为0,countDownLatch.await()就会被唤醒,继续执行

2、CyclicBarrier

加法计数器

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("reach seven");
        });
        for (int i = 0; i < 7; i++) {
            final int temp = i;//跨类获取i
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"--"+temp);
                try {
                    cyclicBarrier.await();//等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }
    }
}

3、Semaphore

限流

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();//得到
                    System.out.println(Thread.currentThread().getName()+"抢到");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"释放");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();//释放
                }
            },String.valueOf(i)).start();
        }
    }
}

原理:

semaphore.acquire();//得到,如果当前已经满了,等待,等待被释放

semaphore.release();//释放,会将当前信号量释放1,然后唤醒等待的线程

作用:多个共享资源互斥的使用,并发限流,控制最大的线程数

读写锁

ReadWriteLock

/**
 * 独占锁(写锁) 一次只能被一个线程占有
 * 共享锁(读锁) 多个线程可以同时占有
 * 读-读 可以共存
 * 读-写 不能共存
 * 写-写 不能共存
 */
public class ReadWriteLockdemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        for (int i = 1; i <=5 ; i++) {
            final int temp = i;
            new Thread(()->{
                myCache.put(temp+"",temp+"");
            },String.valueOf(i)).start();
        }
        for (int i = 1; i <=5 ; i++) {
            final int temp = i;
            new Thread(()->{
                myCache.get(temp+"");
            },String.valueOf(i)).start();
        }
    }
}
class MyCache{
    private Map map = new HashMap<String,String>();
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    //写 写入的时候 只希望只有一个线程在写
    public void put(String key,String value){
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"ok");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }
    //读 所有人的都可以读
    public void get(String key){
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            map.get(key);
            System.out.println(Thread.currentThread().getName()+"ok");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

阻塞队列

不得不阻塞(队列FIFO):

  • 写入:如果队列满了,就必须阻塞等待
  • 取:如果队列是空的,必须阻塞等待生产

使用阻塞队列对的情况:多线程并发处理,线程池

方式抛出异常有返回值阻塞等待超时等待
添加add()offer()put()offer(,)
移除remove()poll()take()poll(,)
判断队首element()peek()--
public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        test1();
        test2();
        test3();
        test4();
    }

    /**
     * 抛出异常
     */
    public static void test1(){
        //队列的大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        //System.out.println(blockingQueue.add("d"));
        System.out.println("================");
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.element());//返回队首元素
    }

    /**
     *有返回值 不抛异常
     */
    public static void test2(){
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("d")); //false 不抛出异常
        System.out.println("============");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());//null 不抛出异常
        System.out.println(blockingQueue.peek());//返回队首元素
    }

    /**
     *等待 阻塞(一直阻塞)
     */
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        //blockingQueue.put("d");//队列没有位置了 一直阻塞
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());//没有元素 一直阻塞
    }

    /**
     *等待 阻塞(等待超时退出)
     */
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("d",2, TimeUnit.SECONDS));//如果队列没有位置就等待两秒 两秒之后还是没有位置就退出
        blockingQueue.poll();
        blockingQueue.poll();
        blockingQueue.poll();
        blockingQueue.poll(2,TimeUnit.SECONDS);//等待超过2秒就退出
    }
}

SynchronousQueue同步队列

没有容量,进去一个元素,必须等待取出来之后,才能再往里边方元素

/**
 * 同步队列
 * 和BlockingQueue不一样 SynchronousQueue不存储元素
 * put了一个元素,必须先从里面take出来,否则不能put值
 */
public class Test02 {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue = new SynchronousQueue<>();
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+"put 1");
                    blockingQueue.put("1");
                    System.out.println(Thread.currentThread().getName()+"put 2");
                    blockingQueue.put("2");
                    System.out.println(Thread.currentThread().getName()+"put 3");
                    blockingQueue.put("3");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"T1").start();
            new Thread(()->{
                try {
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"take"+blockingQueue.take());
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"take"+blockingQueue.take());
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"take"+blockingQueue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"B2").start();
    }
}

线程池

三大方法、七大参数、四种拒绝策略

池化技术

程序运行的本质:占用系统的资源;池化技术可以优化资源的利用

池化技术:事先准备好一些资源,需要用,就去池子里拿,用完之后在扔回池子里

好处

  1. 降低资源的管理
  2. 提高响应的速度
  3. 方便管理

线程复用、可以控制最大并发数、管理线程

三大方法

/**
 * Executors 工具类,三大方法
 * 使用线程池创建线程 使用结束后必须要关闭线程
 */
public class Demo01 {
    public static void main(String[] args) {
        //ExecutorService executorPool = Executors.newSingleThreadExecutor();//单个线程
        //ExecutorService executorPool = Executors.newFixedThreadPool(5);//固定大小的线程池
        ExecutorService executorPool = Executors.newCachedThreadPool();//可变化大小的线程池 遇强则强 遇弱则弱
        try {
            for (int i = 1; i <= 10; i++) {
                executorPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭线程池
            executorPool.shutdown();
        }
    }
}

七大参数

源码

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//约为21亿
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
//本质:ThreadPoolExecutor
    public ThreadPoolExecutor(int corePoolSize,// 核心线程池大小
                              int maximumPoolSize,//最大核心线程池大小
                              long keepAliveTime,//超时了没有人调用线程就会释放
                              TimeUnit unit,//超时单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂,用来创建线程,一般不用动
                              RejectedExecutionHandler handler//拒绝策略
                             ) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

手动创建线程池

public class Demo01 {
    public static void main(String[] args) {
        //自定义线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy()
        );
        try {
            for (int i = 1; i <=10; i++) {
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭线程池
            threadPoolExecutor.shutdown();
        }
    }
}

四种拒绝策略

/**
 * 四种拒绝策略
 * AbortPolicy() 该策略 队列满了之后抛出异常
 * CallerRunsPolicy() 该策略 哪来的回哪去
 * DiscardPolicy() 队列满了,丢掉任务,不会抛出异常
 * DiscardOldestPolicy() 队列满了,抛弃队列里面最老的那个,代替他的位置尝试进入队列里,不会抛出异常
 */

如何设置线程池的最大值

IO密集型、CPU密集型(调优)

public class Demo01 {
    public static void main(String[] args) {
        /**
         * 自定义线程池
         * 最大线程的定义
         * 1、CPU密集型 获取CPU核数 根据核数确定最大线程
         * 2、IO密集型 判断程序中十分耗IO的线程 确定最大线程的时候要比这个值大
         */
        //获取CPU核数
        System.out.println(Runtime.getRuntime().availableProcessors());
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy()
        );
        try {
            for (int i = 1; i <=10; i++) {
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭线程池
            threadPoolExecutor.shutdown();
        }
    }
}

四大函数式接口

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
//FunctionalInterface
//简化编程模型,框架底层大量应用

函数型接口:一个输入参数 一个输出参数

/**
 * Function 函数型接口 有一个输入参数 一个输出参数
 * 只要是函数型接口 可以用lambda表达式简化
 */
public class Demo01 {
    public static void main(String[] args) {
        Function<String, String> function = new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s;
            }
        };
        System.out.println(function.apply("aaa"));

        Function<String,String> function1 = (s)->{return s;};
        System.out.println(function1.apply("qqq"));
    }
}

断定性接口:有一个输入函数 返回值为布尔类型

/**
 * 断定型接口 有一个输入函数 返回值为布尔类型
 */
public class Demo02 {
    public static void main(String[] args) {
        Predicate<String> predicate = new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.isEmpty();
            }
        };
        System.out.println(predicate.test(""));
        Predicate<String> predicate1 = s->{ return s.isEmpty();};
        System.out.println(predicate1.test("1"));
    }
}

Consumer:消费性接口


/**
 * 消费性接口 只有输入 没有返回值
 */
public class Demo03 {
    public static void main(String[] args) {
        Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        consumer.accept("aaa");
        Consumer<String> consumer1 = s -> System.out.println(s);
        consumer1.accept("aaa");
        
    }
}

Supplier:供给型接口

/**
 * 供给型接口 没有参数 只有返回值
 */
public class Demo04 {
    public static void main(String[] args) {
        Supplier<Integer> supplier = new Supplier<Integer>() {
            @Override
            public Integer get() {
                return 1024;
            }
        };
        System.out.println(supplier.get());
        Supplier<Integer> supplier1 = ()-> {return 1024;};
        System.out.println(supplier1.get());
    }
}

Stream流式计算

public class Test01 {
    public static void main(String[] args) {
        User user1 = new User(1, "aa", 21);
        User user2 = new User(2, "bb", 22);
        User user3 = new User(3, "cccc", 23);
        User user4 = new User(4, "dddd", 24);
        User user5 = new User(5, "eee", 25);
        User user6 = new User(6, "f", 26);
        User user7 = new User(7, "gggg", 27);
        List<User> users = Arrays.asList(user1, user2, user3, user4, user5, user6, user7);
        //计算交给stream流
        //stream流式计算、链式编程、lambda表达式、函数式接口
        users.stream()
                .filter(u->{return u.getId()%2==0;})
                .filter((u)->{return u.getAge()>23;})
                .map(u->{return u.getName().toUpperCase();})
                .sorted((u1,u2)->{return u1.compareTo(u2);})
                .limit(1)
                .forEach(System.out::println)
        ;
    }
}

ForkJoin

出现在JDK1.7,并行执行任务,提高效率,大数据量

特点

工作窃取

维护的都是双端队列

操作

/**
 * 如何使用forkjoin
 * 1、通过forkjoinpool执行
 * 2、计算任务forkjoinPool.execute(ForkJoinTask task)
 * 3、计算类要继承ForkJoinTask
 */
public class ForkJoinDemo extends RecursiveTask<Long> {
    private Long start;
    private Long end;
    //临界值
    private Long temp=10000L;
    public ForkJoinDemo(Long start, Long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if ((end-start)<temp){
            Long sum = 0L;
            for (long i = start; i <=end; i++) {
                sum+=i;
            }
            return sum;
        }else {//forkjoin
            Long middle = (start+end)/2;
            //拆分任务 把任务压入线程队列
            ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
            task1.fork();
            ForkJoinDemo task2 = new ForkJoinDemo(middle + 1, end);
            task2.fork();
            return task1.join()+task2.join();
        }
    }
}
public class TestDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
            test3();
    }
    //正常方法
    public static void test1(){
        long start = System.currentTimeMillis();
        Long sum = 0L;
        for (long i = 1; i <= 10_0000_0000; i++) {
            sum+=i;
        }
        long end = System.currentTimeMillis();
        System.out.println("sum="+sum+" time="+(end-start));
    }
    //forkjoin
    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(0L,10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long sum = submit.get();
        long end = System.currentTimeMillis();
        System.out.println("sum="+sum+" time="+(end-start));
    }
    //Stream并行流
    public static void test3(){
        long start = System.currentTimeMillis();
        long sum = LongStream.rangeClosed(0, 10_0000_0000).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("sum="+sum+" time="+(end-start));
    }
}

异步回调

Future设计的初衷:对将来某个事件的结果进行建模

/**
 * 异步调用:CompletableFuture
 * 异步执行
 * 成功回调
 * 失败回调
 */
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //发送一个请求
        /**
        //没有返回值的runAsync异步回调
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
        });
        System.out.println("111");
        //获取执行结果
        completableFuture.get();
**/
        //有返回值的supplyAsync
        CompletableFuture<Integer> s = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+"supplyAsync==>Integer");
            //int a = 10/0;
            return 1024;
        });
        System.out.println(s.whenComplete((t,u)->{
            System.out.println("t"+t);//返回正常的信息
            System.out.println("u"+u);//返回错误的信息
        }).exceptionally((e)->{
            System.out.println(e.getMessage());
            return 233;
        }).get());
    }
}

JMM

Volatile是java虚拟机提供轻量级的同步机制

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

什么事JMM

JMM:java内存模型,不存在的东西,是一种概念、约定

关于JMM的一些同步的约定

  1. 线程解锁前,必须把共享变量立刻刷回主存
  2. 线程加锁前,必须读取主存中的最新值到工作内存
  3. 加锁和解锁是同一把锁

内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)

  • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
  • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
  • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
  • assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
  • store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
  • write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

JMM对这八种指令的使用,制定了如下规则:

  • 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
  • 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
  • 不允许一个线程将没有assign的数据从工作内存同步回主内存
  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
  • 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
  • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
  • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
  • 对一个变量进行unlock操作之前,必须把此变量同步回主内存

JMM对这八种操作规则和对volatile的一些特殊规则就能确定哪里操作是线程安全,哪些操作是线程不安全的了。但是这些规则实在复杂,很难在实践中直接分析。所以一般我们也不会通过上述规则进行分析。更多的时候,使用java的happen-before规则来进行分析。

Volatile

保证可见性

public class JMMDemo {
    //不加volatile 程序就会死循环
    //加volatile保证程序的可见性
    private volatile static int num = 0;
    public static void main(String[] args) {
        new Thread(()->{//加volatile 线程可以感知内存变化 
            while (num==0) {

            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }
}

不保证原子性

原子性:不可分割

线程在执行任务的时候,不能被打扰,也不能被分割。要么同时成功,要么同时失败

public class Demo01 {
    private volatile static int num = 0;
    public static void sum(){
        num++;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    sum();
                }
            }).start();
        }
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"  "+num);
    }
}

**如果不加lock和synchronize没怎样保证原子性 **

使用原子类,解决原子性问题

public class Demo01 {
    private  static AtomicInteger num = new AtomicInteger();
    public static void sum(){
        num.getAndIncrement();//AtomicINteger +1方法 CAS
    }
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    sum();
                }
            }).start();
        }
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"  "+num);
    }
}

这些类的底层都直接和操作系统挂钩,在内存中修改值。Unsafe类是一个很特殊的存在

指令重排

编写的程序,计算机并不是按照你写的那样去执行的

源代码–>编译器优化的重排–>指令并行也可能会重排–>内存系统也会重排–>执行

处理器字进行指令重排的时候,考虑数据之间的依赖性

Volatile可以避免指令重排:

内存屏障 CPU指令 作用:

  1. 保证特定的操作的执行顺序
  2. 可以保证某些变量的内存可见性(利用这些特性Volatile实现了可见性)

Volatile是可以保持可见性,不能保持原子性,由于内存屏障,可以保证避免指令重排的现象产生

单例模式

饿汉式

//饿汉单例模式
public class Hungry {
    //可能会浪费内存空间
    private Hungry(){

    }
    private final static Hungry Hungry = new Hungry();
    public static Hungry getInstance(){
        return Hungry;
    }
}

DCL懒汉式

//懒汉式单例模式
public class LazyMan {
    private static boolean flag = false;
    private LazyMan(){
        synchronized (LazyMan.class){
            if (flag==false){
                flag = true;
            }else {
                throw new RuntimeException("stop now");
            }
//            if (lazyMan!=null){
//                throw new RuntimeException("stop now");
//            }
        }
    }
    private volatile static LazyMan lazyMan;//volatile避免指令重排

    /**
     * 双重检测锁模式的 懒汉单例 DCL懒汉式
     */
    public static LazyMan getInstance(){
        if (lazyMan==null){
            synchronized (LazyMan.class){
                if (lazyMan==null){
                    lazyMan =  new LazyMan();//不是一个原子性操作
                    /**
                     * 1、分配内存空间
                     * 2、执行构造方法,初始化对象
                     * 3、把这个对象指向这个空间
                     *
                     * CPU中的执行顺序:可能123 132 会产生指令重排现象
                     */
                }
            }
        }
        return lazyMan;
    }

//反射
    public static void main(String[] args) throws Exception {
        //LazyMan lazyMan = LazyMan.getInstance();
        Field flag = LazyMan.class.getDeclaredField("flag");
        flag.setAccessible(true);
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan lazyMan1 = declaredConstructor.newInstance();
        flag.set(lazyMan1,false);
        LazyMan lazyMan = declaredConstructor.newInstance();
        System.out.println(lazyMan);
        System.out.println(lazyMan1);
    }
}

静态内部类

//静态内部类
public class Holder {
    private Holder(){

    }
    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }
    public static class InnerClass{
        private static final Holder HOLDER = new Holder();
    }
}

单例不安全,因为有反射 采用枚举

//enum 本身是一个Class类
public enum  EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}
class Test{
    public static void main(String[] args) throws Exception {
        EnumSingle instance = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);//看源码 发现是有一个参构造器
        declaredConstructor.setAccessible(true);
        EnumSingle instance1 = declaredConstructor.newInstance();

    }
}

枚举类型的最终反编译源码

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumSingle.java

package com.wm.single;


public final class EnumSingle extends Enum
{

    public static EnumSingle[] values()
    {
        return (EnumSingle[])$VALUES.clone();
    }

    public static EnumSingle valueOf(String name)
    {
        return (EnumSingle)Enum.valueOf(com/wm/single/EnumSingle, name);
    }

    private EnumSingle(String s, int i)
    {
        super(s, i);
    }

    public EnumSingle getInstance()
    {
        return INSTANCE;
    }

    public static final EnumSingle INSTANCE;
    private static final EnumSingle $VALUES[];

    static 
    {
        INSTANCE = new EnumSingle("INSTANCE", 0);
        $VALUES = (new EnumSingle[] {
            INSTANCE
        });
    }
}

深入理解CAS

CAS

/**
 * CAS compareAndSet 比较并交换
 */
public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);


        //期望、更新
        //public final boolean compareAndSet(int expect, int update)
        //如果期望达到了 就更新 CAS是CPU的并发原语
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        atomicInteger.getAndIncrement();
        System.out.println(atomicInteger.get());

    }
}

Unsafe类

CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么执行操作,如果不是就一直循环

缺点:

  1. 循环耗时
  2. 一次性只能保证一个共享变量的原子性操作
  3. ABA问题

CAS:ABA问题

原子引用

解决ABA问题 思想:乐观锁

带版本号的原子操作

public class CASDemo02 {
    //注意:如果泛型是一个包装类,注意对象的引用问题
    static AtomicStampedReference<Integer> integerAtomicStampedReference = new AtomicStampedReference<>(1, 1);
    public static void main(String[] args) {
        new Thread(()->{
            int stamp = integerAtomicStampedReference.getStamp();//获取版本号
            System.out.println("a1->"+stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(integerAtomicStampedReference.compareAndSet(1, 2, integerAtomicStampedReference.getStamp(), integerAtomicStampedReference.getStamp() + 1));
            System.out.println("a2->"+integerAtomicStampedReference.getStamp());
            System.out.println(integerAtomicStampedReference.compareAndSet(2, 1, integerAtomicStampedReference.getStamp(), integerAtomicStampedReference.getStamp() + 1));
            System.out.println("a3->"+integerAtomicStampedReference.getStamp());
        },"a").start();
        //和乐观锁的原理相同
        new Thread(()->{
            int stamp = integerAtomicStampedReference.getStamp();//获取版本号
            System.out.println("b1->"+stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(integerAtomicStampedReference.compareAndSet(1, 6, integerAtomicStampedReference.getStamp(), integerAtomicStampedReference.getStamp() + 1));
            System.out.println("b2->"+integerAtomicStampedReference.getStamp());
        },"b").start();

    }
}

各种锁的理解

1、公平锁、非公平锁

公平锁:非常公平,不能插队,必须先来后到

非公平锁:非常不公平,可以插队(默认都是非公平锁)

    public ReentrantLock() {
        sync = new NonfairSync();
    }
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

2、可重入锁

可重入锁(递归锁)

synchronize

public class Demo02 {
    public static void main(String[] args) {
        Phone1 phone = new Phone1();
        new Thread(()->{
            phone.send();
        },"a").start();
        new Thread(()->{
            phone.send();
        },"b").start();
    }
}
class Phone1{
    public synchronized  void send(){

            System.out.println(Thread.currentThread().getName()+"send");
            message();
    }
    public synchronized  void message(){

            System.out.println(Thread.currentThread().getName()+"message");
    }
}

lock

public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            phone.send();
        },"a").start();
        new Thread(()->{
            phone.send();
        },"b").start();
    }
}
class Phone{
    Lock lock = new ReentrantLock();
    public  void send(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"send");
            message();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public  void message(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"message");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

3、自旋锁

spinlock

自定义锁

public class LockDemo {
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    public void myLock(){
        Thread thread = Thread.currentThread();
        //自旋锁
        while (!atomicReference.compareAndSet(null,thread));
        System.out.println(Thread.currentThread().getName()+"==>myLock");

    }
    public void UnMyLock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
        System.out.println(Thread.currentThread().getName()+"==>unmyLock");

    }
}
public class TestSpinLock {
    public static void main(String[] args) {
        LockDemo lockDemo = new LockDemo();
        new Thread(()->{
            lockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lockDemo.UnMyLock();
            }

        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            lockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lockDemo.UnMyLock();
            }

        },"B").start();

    }
}

A拿到锁后,没有解锁,B拿不到锁,B自旋,知道A释放锁后,B拿到锁后停止自旋

4、死锁

public class DeadLockDemo {
    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";
        new Thread(new MyThread(lockA, lockB),"A").start();
        new Thread(new MyThread(lockB, lockA),"B").start();
    }
}
class MyThread implements Runnable{
    private String lockA;
    private String lockB;

    public MyThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized (lockA){
            System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"want"+lockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"want"+lockA);
            }
        }
    }
}

A线程拿到lockA想要lockB,但是此时B拿到lockB想要lockA,两个人都不放下手上的但是都要获取对方的,死锁

这里虽然new了两个Tread,但是因为锁的是string,在常量池(可以理解为整个程序共享),所以两个线程锁的是同一个string对象,也就是同一把锁。

解决问题

  1. 使用结束jsp -l定位进程号

  2. 使用jstack 进程号 找到死锁问题

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值