JUC学习笔记,Volatile,CAS,单例模式详解

2 篇文章 0 订阅
2 篇文章 0 订阅

JUC学习笔记

unsplash-Luis Quintero

锁的基本区别

lock与synchronized的示例

传统 synchronized

public class SaleTicketDemo01 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(() -> { for (int i = 0; i < 10000; i++) ticket.sale();}).start();
        new Thread(() -> { for (int i = 0; i < 10000; i++) ticket.sale();}).start();
        new Thread(() -> { for (int i = 0; i < 10000; i++) ticket.sale();}).start();
    }
}

class Ticket {
    private Integer ticketCount = 10000;

    public synchronized void sale() {
        if (ticketCount > 0) {
            System.out.println("*****"+Thread.currentThread().getName()+"线程成功的卖出了票"+(ticketCount--)+"号,还剩了:"+ticketCount+"张");
        }
    }
}

LOCK锁

image-20200729110541109

公平与非公平锁区别 : 是否可以插队执行

public class SaleTicketDemo02 {
    public static void main(String[] args) {
        Ticket02 ticket02 = new Ticket02();


        new Thread(() -> { for (int i = 0; i < 10000; i++) ticket02.sale();}).start();
        new Thread(() -> { for (int i = 0; i < 10000; i++) ticket02.sale();}).start();
        new Thread(() -> { for (int i = 0; i < 10000; i++) ticket02.sale();}).start();
    }
}

class Ticket02 {
    private Integer ticketCount = 10000;

    Lock lock = new ReentrantLock();

    public void sale() {
        lock.lock();
        try {
            if (ticketCount > 0) {
                System.out.println("*****" + Thread.currentThread().getName() + "线程成功的卖出了票" + (ticketCount--) + "号,还剩了:" + ticketCount + "张");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}
lock与synchronized的区别
  1. synchronized 是java提供的关键字 而lock是一个类
  2. synchronized 无法获取锁的状态 而lock可以
  3. synchronized 会自动释放锁 而lock是手动的 不释放会会造成死锁
  4. synchronized 线程1获得锁并且阻塞的话,之后的线程会一直等待锁,而lock不会一直等待
  5. synchronized 可重入锁,不可中断的,非公平锁 lock 可重入锁,可以判断锁状态中断,非公平锁(默认,可切换为公平锁)
  6. synchronized 适合锁少量的同步代码 lock 可以锁大量的同步代码块

生产者与消费者问题

synchronized 示例
public class A {
    public static void main(String[] args) {
        Data data = new Data();

        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 Data {
    private Integer content = 0;

    public synchronized void increment() throws InterruptedException {
        if (content != 0) {
            this.wait();
        }
        content++;
        System.out.println(Thread.currentThread().getName()+"***当前线程操作结果:"+content);
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        if (content == 0) {
            this.wait();
        }
        content--;
        System.out.println(Thread.currentThread().getName()+"***当前线程操作结果:"+content);
        this.notifyAll();
    }
}

在使用if判断等待时出现虚假唤醒问题

image-20200729141249135

package com.rigel.pc;

/**
 * @Author: Zhou
 * @Description:
 * @Date: Create in 13:59 2020/7/29
 * @modify By:
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();

        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 Data {
    private Integer content = 0;

    public synchronized void increment() throws InterruptedException {
        while (content != 0) {
            this.wait();
        }
        content++;
        System.out.println(Thread.currentThread().getName()+"***当前线程操作结果:"+content);
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        while (content == 0) {
            this.wait();
        }
        content--;
        System.out.println(Thread.currentThread().getName()+"***当前线程操作结果:"+content);
        this.notifyAll();
    }
}

JUC lock示例
与synchronize方法上的区别

image-20200729143051248

代码 (与上面的synchronize实现的功能没有区别)

public class B {
    public static void main(String[] args) {
        Datab data = new Datab();

        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 Datab {
    private Integer content = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (content != 0) {
                //等待
                condition.await();
            }
            content++;
            System.out.println(Thread.currentThread().getName() + "***当前线程操作结果:" + content);
            //唤醒全部
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (content == 0) {
                condition.await();
            }
            content--;
            System.out.println(Thread.currentThread().getName() + "***当前线程操作结果:" + content);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock(); 
        }

    }
}
Condition的优势

Condition 可以精准的通知和唤醒某个线程

上面使用synchronize实现的,以及刚刚lock实现的我们不能控制线程的执行顺序,它们是随机执行的。

代码

public class C {
    public static void main(String[] args) {
        DataC data = new DataC();
        //精准通知
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printB();
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printC();
            }
        }, "C").start();
    }
}

class DataC {
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    //1A  2B  3C
    private Integer numInteger = 1;


    public void printA() {
        lock.lock();
        try {
            while (numInteger != 1) {
                condition1.await();
            }
            numInteger = 2;
            System.out.println(Thread.currentThread().getName() + "====aaaaa");
            //指定唤醒谁
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB() {
        lock.lock();
        try {
            while (numInteger != 2) {
                condition2.await();
            }
            numInteger = 3;
            System.out.println(Thread.currentThread().getName() + "====bbbbb");
            //指定唤醒谁
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            while (numInteger != 3) {
                condition3.await();
            }
            numInteger = 1;
            System.out.println(Thread.currentThread().getName() + "====ccccc");
            //指定唤醒谁
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

8锁问题

深刻理解锁
/**
 * 1、标准情况下,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话
 * 1、sendSms延迟4秒,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话
 */
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        //锁的存在
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();
        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone.call();
        }, "B").start();
    }
}

class Phone {
    // synchronized 锁的对象是方法的调用者!、
    // 两个方法用的是同一个锁,谁先拿到谁执行!
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }
}
/**
 * 3、 增加了一个普通方法后!先执行发短信还是Hello? 普通方法
 * 4、 两个对象,两个同步方法, 发短信还是 打电话? // 打电话
 */
public class Test2 {
    public static void main(String[] args) {
        // 两个对象,两个调用者,两把锁!
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();
        //锁的存在
        new Thread(() -> {
            phone1.sendSms();
        }, "A").start();
        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }
}

class Phone2 {
    // synchronized 锁的对象是方法的调用者!
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }

    // 这里没有锁!不是同步方法,不受锁的影响
    public void hello() {
        System.out.println("hello");
    }
}
/**
 * 5、增加两个静态的同步方法,只有一个对象,先打印 发短信   打电话
 * 6、两个对象!增加两个静态的同步方法, 先打印 发短信  打电话
 */
public class Test3 {
    public static void main(String[] args) {
        // 两个对象的Class类模板只有一个,static,锁的是Class
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();
        //锁的存在
        new Thread(() -> {
            phone1.sendSms();
        }, "A").start();
        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone2.call();
        }, "B").start();
    }
}

// Phone3唯一的一个 Class 对象
class Phone3 {
    // synchronized 锁的对象是方法的调用者!
    // static 静态方法
    // 类一加载就有了!锁的是Class
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call() {
        System.out.println("打电话");
    }
}
package com.rigel;

import java.util.concurrent.TimeUnit;


/**
 * 7、1个静态的同步方法,1个普通的同步方法 ,一个对象,先打印 打电话 发短信
 * 8、1个静态的同步方法,1个普通的同步方法 ,两个对象,先打印 打电话 发短信 
 */
public class Test4 {
    public static void main(String[] args) {
        // 两个对象的Class类模板只有一个,static,锁的是Class
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        //锁的存在
        new Thread(()->{
            phone1.sendSms();
        },"A").start();
        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}
// Phone3唯一的一个 Class 对象
class Phone4{
    // 静态的同步方法 锁的是 Class 类模板
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    // 普通的同步方法 锁的调用者
    public synchronized void call(){
        System.out.println("打电话");
    }
}


集合的线程安全问题

List 的不安全解决方案

解决方案 3种解决方案

 // java.util.ConcurrentModificationException 并发修改异常!
public class ListTest {    public static void main(String[] args) {        
  // 并发下 ArrayList 不安全的吗,Synchronized;       
  /**         
  * 解决方案;         
  * 1、List<String> list = new Vector<>();         
  * 2、List<String> list = Collections.synchronizedList(new ArrayList<>());         
  * JUC         
  * 3、List<String> list = new CopyOnWriteArrayList<>();         
  */        
  // CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略;        
  // 多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)        
  // 在写入的时候避免覆盖,造成数据问题!        
  // 读写分离        
  // CopyOnWriteArrayList  与 Vector 区别为什么要用前者?        
  // 因为CopyOnWriteArrayList 内部使用的是lock锁,而Vector用的是synchronized锁效率比较低
  List<String> list = new CopyOnWriteArrayList<>();        
  for (int i = 1; i <= 10; i++) {            
    new Thread(() -> {                	
      list.add(UUID.randomUUID().toString().substring(0, 5));                											System.out.println(list);     
    }, String.valueOf(i)).start();       
  }    
}
                       
                      }
Set 的不安全解决方案

解决方案

与List基本一样 2中解决方案   
1. Set<String> collections = Collections.synchronizedSet(new HashSet<>());  
//JUC  
2. Set<String> stringCopyOnWriteArraySet = new CopyOnWriteArraySet<String>();java
Map 的不安全解决方案
// ConcurrentModificationException
public class MapTest {
	public static void main(String[] args) {
      // 默认等价于 new HashMap<>(16,0.75);
      // Map<String, String> map = new HashMap<>();
      Map<String, String> map = new ConcurrentHashMap<>();
      for (int i = 1; i <=30; i++) {
          new Thread(()->{
          map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(
          0,5));
          System.out.println(map);
      	},String.valueOf(i)).start();
      }
   }
}

JAVA线程的另一种开启方式(Callable)

Callable

ju

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

1

2

/**
 * 1、探究原理
 * 2、觉自己会用
 */
public class CallableTest {
    public static void main(String[] args) throws ExecutionException,
            InterruptedException {
        // new Thread(new Runnable()).start();
        // new Thread(new FutureTask<V>()).start();
        // new Thread(new FutureTask<V>( Callable )).start();
        new Thread().start(); // 怎么启动Callable
        MyThread thread = new MyThread();
        FutureTask futureTask = new FutureTask(thread); // 适配类
        new Thread(futureTask, "A").start();
        new Thread(futureTask, "B").start(); // 结果会被缓存,效率高
        Integer o = (Integer) futureTask.get(); //这个get 方法可能会产生阻塞!把他放到最后
        // 或者使用异步通信来处理!
        System.out.println(o);
    }
}

class MyThread implements Callable<Integer> {
    @Override
    public Integer call() {
        System.out.println("call()"); // 会打印几个call
        // 耗时的操作
        return 1024;
    }
}

1、有缓存

2、结果可能需要等待,会阻塞


JUC常用辅助类

CountDownLatch(减法计数器)
//计数器
public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        //总数为6
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName()+"Go Out");
                //减1
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }
        //等待计数器归零在向下继续执行
        countDownLatch.await();

        System.out.println("关门了。。。。");
    }
}

主要方法

  1. countDownLatch.countDown() 计数器减一操作
  2. countDownLatch.await() 等待计数器归零后继续往下执行(等待时阻塞转态)
CyclicBarrier(加法计数器)

与上面正好相反,上面为减法计数器,这个为加法计数器(但是用法不太一样)

public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
            System.out.println("计数器已经到5了,结束了");
        });

        for (int i = 1; i <= 5; i++) {
            final int temp  = i;
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName()+"线程正在操作......现在操作了"+temp+"次了");
                    cyclicBarrier.await(); //线程会在这个地方进行等待,直到上面的计数器到达了五个才继续执行该线程的其他内容
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

主要方法

  1. cyclicBarrier.await()

线程会在这个地方进行等待,直到上面的计数器到达了五个才继续执行该线程的其他内容

Semaphore(信号量)
public class SemaphoreTest {
    public static void main(String[] args) {

        //参数一:控制的总线程数  参数二:公平锁or非公平锁
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                try {
                    //得到并占用
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"  线程得到了占用权");
                    try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
                    System.out.println(Thread.currentThread().getName()+"  线程占用权使用完毕,马上在finally中归还占用");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                }
            }).start();
        }
    }
}

主要方法

  1. semaphore.acquire() 得到
  2. semaphore.release() 释放

控制的总线程数为3,当线程数使用semaphore.acquire()方法得到时就会占用1个,当3个全部占用完毕时执行能进行排队,直到semaphore.release()方法释放之后才能得到。


读写锁

public class ReadWriteLockTest {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();

        for (int i = 0; i < 6; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.put(temp + "", temp + "");
            }, String.valueOf(i)).start();
        }

        for (int i = 0; i < 6; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.get(temp + "");
            }, String.valueOf(i)).start();
        }

    }

}

class MyCache {

    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    private volatile Map<String, Object> maps = new HashMap<String, Object>();

    //写入时只希望同时只能有1个线程进行写操作
    public void put(String key, Object value) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "写入" + key);
            maps.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入完成" + key);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }

    }

    //读的时候,可以无限
    public Object get(String key) {
        readWriteLock.readLock().lock();
        Object o = null;
        try {
            System.out.println(Thread.currentThread().getName() + "读取" + key);
            o = maps.get(key);
            System.out.println(Thread.currentThread().getName() + "读取完成,结果:  " + o);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }

        return o;
    }
}

主要方法

  1. readWriteLock.writeLock().lock() 加写锁操作,一次只能有1个线程操作
  2. readWriteLock.writeLock().unlock() 解写读锁操作
  3. readWriteLock.readLock().lock() 加读锁操作,可以有多线程同时操作读取
  4. readWriteLock.readLock().unlock() 解读锁操作

另一种说法

共享锁(读锁)

独占锁(写锁)


BlockingQueue阻塞队列

四组API
方式抛出异常不抛出异常,有返回值阻塞等待超时等待
添加add()offer()put()offer(E,long,TimeUnit)
移除remove()poll()take()poll(long,TimeUnit)
取出队列首个元素element()peek()--
/**
* 抛出异常
*/
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"));
    // IllegalStateException: Queue full 抛出异常!
    // System.out.println(blockingQueue.add("d"));
    System.out.println("=-===========");
    System.out.println(blockingQueue.remove());
    System.out.println(blockingQueue.remove());
    System.out.println(blockingQueue.remove());
    // java.util.NoSuchElementException 抛出异常!
    // System.out.println(blockingQueue.remove());
}
/**
* 不抛出异常,有返回值
*/
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 不抛出异常!
}
/**
* 等待,阻塞(一直阻塞)
*/
public static void test3() throws InterruptedException {
    // 队列的大小
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
    //SynchronousQueue 同步队列
    //没有容量,
    //进去一个元素,必须等待取出来之后,才能再往里面放一个元素!
    //put、take
    // 一直阻塞
    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);
    blockingQueue.offer("a");
    blockingQueue.offer("b");
    blockingQueue.offer("c");
    // blockingQueue.offer("d",2,TimeUnit.SECONDS); // 等待超过2秒就退出
    System.out.println("===============");
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    blockingQueue.poll(2,TimeUnit.SECONDS); // 等待超过2秒就退出
}

SynchronousQueue同步队列

没有长度,只能放一个,取一个,才能再放下一个.

put() 放 take() 取

/**
* 同步队列
* 和其他的BlockingQueue 不一样, SynchronousQueue 不存储元素
* put了一个元素,必须从里面先take取出来,否则不能在put进去值!
*/
public class SynchronousQueueDemo {
    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(3);
              System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
              TimeUnit.SECONDS.sleep(3);
              System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
              TimeUnit.SECONDS.sleep(3);
              System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
            } catch (InterruptedException e) {
          	  e.printStackTrace();
            }
        },"T2").start();
    }
}

线程池

关键: 三大方法、7大参数、4种拒绝策略

线程池的好处

1、降低资源的消耗

2、提高响应的速度

3、方便管理

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

3大方法

public class excutors {

    public static void main(String[] args) {
        ExecutorService executorService = 																Executors.newSingleThreadExecutor(); //单个线程池
        Executors.newFixedThreadPool(5);  //固定的线程池
        Executors.newCachedThreadPool();  //可伸缩的线程池

        for (int i = 0; i < 100; i++) {
            executorService.execute(() -> {
                System.out.println(Thread.currentThread().getName());
            });
        }
    }
}

7大参数

//源码分析
public class excutors {


    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(5, 5,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());
    }

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>());
    }

    // 本质ThreadPoolExecutor()
    public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
                              int maximumPoolSize, // 最大核心线程池大小
                              long keepAliveTime, // 超时了没有人调用就会释放
                              TimeUnit unit, // 超时单位
                              BlockingQueue<Runnable> workQueue, // 阻塞队列
                              ThreadFactory threadFactory, // 线程工厂:创建线程的,一般不用动
                              RejectedExecutionHandler handle // 拒绝策略
    ) {
        if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw newIllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
}

4种拒绝策略

/**
 * new ThreadPoolExecutor.AbortPolicy() // 队列满了,丢掉任务,抛出异常
 * new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!
 * new ThreadPoolExecutor.CallerRunsPolicy() // 呢个线程调用的让那个线程去处理
 * new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和最早的竞争,不会抛出异常
 */
public class excutors {
  
    public static void main(String[] args) {

        ExecutorService executorService = Executors.newSingleThreadExecutor();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        try {
            for (int i = 0; i < 16; i++) {
                threadPoolExecutor.execute(() -> {

                    System.out.println(Thread.currentThread().getName());
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPoolExecutor.shutdown();
        }

    }
}

aqq

/**
* new ThreadPoolExecutor.AbortPolicy() // 银行满了,还有人进来,不处理这个人的,抛出异
常
* new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里!
* new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!
* new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和最早的竞争,也不会
抛出异常!
*/

重点备注信息

池的最大的大小如何去设置

IO密集型,CPU密集型

public class Demo01 {
    public static void main(String[] args) {
        // 自定义线程池!工作 ThreadPoolExecutor
        // 最大线程到底该如何定义
        // 1、CPU 密集型,几核,就是几,可以保持CPu的效率最高!
        // 2、IO 密集型 > 判断你程序中十分耗IO的线程,
        // 程序 15个大型任务 io十分占用资源!
        // 获取CPU的核数
        System.out.println(Runtime.getRuntime().availableProcessors());

        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(),
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试去和最早的竞争,也不会抛出异常!
        try {
            // 最大承载:Deque + max
            // 超过 RejectedExecutionException
            for (int i = 1; i <= 9; i++) {
            // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

ForkJoin

将大数据量任务进行拆分,拆分成多个小人物,最后进行合并,得到最终结果

ForkJoin特点: 工作窃取

里面维护的全部都是双端队列

ForkJoin例子

/**
* 求和计算的任务!
* // 如何使用 forkjoin
* // 1、forkjoinPool 通过它来执行
* // 2、计算任务 forkjoinPool.execute(ForkJoinTask task)
* // 3. 计算类要继承 ForkJoinTask
*/
public class ForkJoinDemo extends RecursiveTask<Long> {
    private Long start; // 1
    private Long end; // 1990900000
    // 临界值
    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 Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // test1(); // 12224
        // test2(); // 10038
        // test3(); // 153
    }
    // 普通程序员
    public static void test1(){
          Long sum = 0L;
          long start = System.currentTimeMillis();
          for (Long i = 1L; i <= 10_0000_0000; i++) {
              sum += i;
          }
          long end = System.currentTimeMillis();
          System.out.println("sum="+sum+" 时间:"+(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+" 时间:"+(end-start));
      }
     public static void test3(){
        long start = System.currentTimeMillis();
        // Stream并行流 () (]
        long sum = LongStream.rangeClosed(0L,
        10_0000_0000L).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("sum="+"时间:"+(end-start));
    }
}

异步回调

/**
* 异步调用: CompletableFuture
* // 异步执行
* // 成功回调
* // 失败回调
*/
public class Demo01 {
    public static void main(String[] args) throws ExecutionException,
    InterruptedException {
        // 没有返回值的 runAsync 异步回调
        // CompletableFuture<Void> completableFuture =
        CompletableFuture.runAsync(()->{
            // try {
            // TimeUnit.SECONDS.sleep(2);
            // } catch (InterruptedException e) {
            // e.printStackTrace();
            // }
            //
            System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
            // });
            //
            // System.out.println("1111");
            //
            // completableFuture.get(); // 获取阻塞执行结果
            // 有返回值的 supplyAsync 异步回调
            // ajax,成功和失败的回调
            // 返回的是错误信息;
            CompletableFuture<Integer> completableFuture =
            CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
            int i = 10/0;
            return 1024;
        });
        System.out.println(completableFuture.whenComplete((t, u) -> {
            System.out.println("t=>" + t); // 正常的返回结果
            System.out.println("u=>" + u); // 错误信息:
            //java.util.concurrent.CompletionException: java.lang.ArithmeticException: by zero
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return 233; // 可以获取到错误的返回结果
        }).get());
        /**
        * succee Code 200
        * error Code 404 500
        */
    }
}

JMM

JMM : Java内存模型,实际不存在的,概念,约定

关于JMM的一些同步的约定:

1、线程解锁前,必须把共享变量立刻刷回主存。

2、线程加锁前,必须读取主存中的最新值到工作内存中!

3、加锁和解锁是同一把锁

线程 工作内存 、主内存

内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于doublelong类
型的变量来说,load、store、read和write操作在某些平台上允许例外)
  
1.lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
2.unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量
才可以被其他线程锁定
3.read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便
随后的load动作使用
4.load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
5.use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机
遇到一个需要使用到变量的值,就会使用到这个指令
6.assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变
量副本中
7.store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,
以便后续的write使用
8.write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内
存的变量中
//------------------------------------------------------------------------------------------
JMM对这八种指令的使用,制定了如下规则:

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

Volatile

1.保证可见性

public class JMMDemo {
    // 不加 volatile 程序就会死循环!
    // 加 volatile 可以保证可见性,在主内存的参数发生变化后,其他线程的内存可见
    private volatile static int num = 0;

    public static void main(String[] args) { // main
        new Thread(()->{ // 线程 1 对主内存的变化不知道的
            	while (num==0){
           	 }
            }).start();
      
            try {
            		TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
          		  e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }
}

2.不保证原子性

// volatile 不保证原子性
public class VDemo02 {
    // volatile 不保证原子性
    private volatile static int num = 0;
  
    public static void add(){
 		   num++;
    }
  
    public static void main(String[] args) {
        //理论上num结果应该为 2 万
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
            for (int j = 0; j < 1000 ; j++) {
            	add();
            }
            }).start();
        }
            while (Thread.activeCount()>2){ // main gc
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

使用原子类解决这个问题

// volatile 不保证原子性
public class VDemo02 {
  // volatile 不保证原子性
  // 原子类的 Integer
  private volatile static AtomicInteger num = new AtomicInteger();
  
  public static void add(){
    // num++; // 不是一个原子性操作
    num.getAndIncrement(); // AtomicInteger + 1 方法, CAS
  }
  
  public static void main(String[] args) {
    //理论上num结果应该为 2 万
    for (int i = 1; i <= 20; i++) {
      new Thread(()->{
      for (int j = 0; j < 1000 ; j++) {
     	 add();
      }
      }).start();
    }
    while (Thread.activeCount()>2){ // main gc
    	Thread.yield();
    }
    System.out.println(Thread.currentThread().getName() + " " + num);
  }
}

单例模式(深入)

饿汉式

// 饿汉式单例
public class Hungry {
    // 可能会浪费空间
    private byte[] data1 = new byte[1024*1024];
    private byte[] data2 = new byte[1024*1024];
    private byte[] data3 = new byte[1024*1024];
    private byte[] data4 = new byte[1024*1024];
  
    private Hungry(){
    }
  
    private final static Hungry HUNGRY = new Hungry();
  
    public static Hungry getInstance(){
    		return HUNGRY;
    }
}

DCL懒汉式

public class LazyMan {
  private static boolean qinjiang = false;
  
  private LazyMan(){
    synchronized (LazyMan.class){
      if (qinjiang == false){
     		qinjiang = true;
      }else {
      	throw new RuntimeException("不要试图使用反射破坏异常");
      }
    }
  }
  
  private volatile static LazyMan lazyMan;
  // 双重检测锁模式的 懒汉式单例 DCL懒汉式
  
  public static LazyMan getInstance(){
    if (lazyMan==null){
      synchronized (LazyMan.class){
        if (lazyMan==null){
        	lazyMan = new LazyMan(); // 不是一个原子性操作
        }
      }
    }
    return lazyMan;
  }
  
  // 反射!
  public static void main(String[] args) throws Exception {
    // LazyMan instance = LazyMan.getInstance();
    Field qinjiang = LazyMan.class.getDeclaredField("qinjiang");
    qinjiang.setAccessible(true);
    Constructor<LazyMan> declaredConstructor =
    LazyMan.class.getDeclaredConstructor(null);
    declaredConstructor.setAccessible(true);
    LazyMan instance = declaredConstructor.newInstance();
    qinjiang.set(instance,false);
    LazyMan instance2 = declaredConstructor.newInstance();
    System.out.println(instance);
    System.out.println(instance2);
  }
}
/**
* 1. 分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
*
* 123
* 132 A
* B // 此时lazyMan还没有完成构造
*/

静态内部类

// 静态内部类
public class Holder {
  private Holder(){
  }
  
  public static Holder getInstace(){
  	return InnerClass.HOLDER;
  }
  
  public static class InnerClass{
 	 private static final Holder HOLDER = new Holder();
  }
}

枚举单例

public enum EnumSingle {
  INSTANCE;
  public EnumSingle getInstance(){
  	return INSTANCE;
  }
}
  class Test{
  public static void main(String[] args) throws NoSuchMethodException,
  IllegalAccessException, InvocationTargetException, InstantiationException {
    EnumSingle instance1 = EnumSingle.INSTANCE;
    Constructor<EnumSingle> declaredConstructor =
    EnumSingle.class.getDeclaredConstructor(String.class,int.class);
    declaredConstructor.setAccessible(true);
    EnumSingle instance2 = declaredConstructor.newInstance();
    // NoSuchMethodException: com.kuang.single.EnumSingle.<init>()
    System.out.println(instance1);
    System.out.println(instance2);
  }
}

CAS(深入)

CAS

public class CASDemo {
  // CAS compareAndSet : 比较并交换!
  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));
    System.out.println(atomicInteger.get());
    atomicInteger.getAndIncrement()
    System.out.println(atomicInteger.compareAndSet(2020, 2021));
    System.out.println(atomicInteger.get());
  }
}

Unsafe类

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

缺点:

1、 循环会耗时

2、一次性只能保证一个共享变量的原子性

3、ABA问题

CAS的ABA问题

public class CASDemo {
  // CAS compareAndSet : 比较并交换!
  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));
    System.out.println(atomicInteger.get());
    System.out.println(atomicInteger.compareAndSet(2021, 2020));
    System.out.println(atomicInteger.get());
    // ============== 期望的线程 ==================
    System.out.println(atomicInteger.compareAndSet(2020, 6666));
    System.out.println(atomicInteger.get());
  }
}

原子引用

解决ABA 问题,引入原子引用! 对应的思想:乐观锁!

带版本号的原子操作,解决ABA问题

public class CASDemo {
  //AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题
  // 正常在业务操作,这里面比较的都是一个个对象
  static AtomicStampedReference<Integer> atomicStampedReference = new
  AtomicStampedReference<>(1,1);
  // CAS compareAndSet : 比较并交换!
  public static void main(String[] args) {
    new Thread(()->{
      int stamp = atomicStampedReference.getStamp(); // 获得版本号
      System.out.println("a1=>"+stamp);
      try {
         TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      atomicStampedReference.compareAndSet(1, 2,
      atomicStampedReference.getStamp(),
      atomicStampedReference.getStamp() + 1);
      System.out.println("a2=>"+atomicStampedReference.getStamp());
      System.out.println(atomicStampedReference.compareAndSet(2, 1,
      atomicStampedReference.getStamp(),
      atomicStampedReference.getStamp() + 1));
      System.out.println("a3=>"+atomicStampedReference.getStamp());
    },"a").start();
    // 乐观锁的原理相同!
    new Thread(()->{
      int stamp = atomicStampedReference.getStamp(); // 获得版本号
      System.out.println("b1=>"+stamp);
      try {
      	TimeUnit.SECONDS.sleep(2);
      } catch (InterruptedException e) {
   		   e.printStackTrace();
      }
      System.out.println(atomicStampedReference.compareAndSet(1, 6,
      stamp, stamp + 1));
      System.out.println("b2=>"+atomicStampedReference.getStamp());
    },"b").start();
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值