JUC01

1. JUC

java.util:工具包

2. 线程和进程

线程,进程,如果不能使用一句话说出来的技术,不扎实

进程:一个程序,QQ.exe Music.exe 程序的集合

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

Java默认有几个线程?2个 main线程和GC线程。

并发,并行

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

  • CPU一核,模拟出多条线程,快速交替。

并行

  • CPU多核,多个线程可以同时执行;线程池。
// 获取cpu的核数
// CPU密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());

wait不需要捕获异常???存疑

Lock锁(重点)

传统Synchronized

public class SaleTicketDemo01 {
    public static void main(String[] args) {
        // 并发:多个线程操作同一个资源类,把资源类丢入线程
        Ticket ticket = new Ticket();

        new Thread(() ->{
            for (int i = 0; i < 20; i++) {
                ticket.sale();
            }
        }, "A").start();
        new Thread(() ->{
            for (int i = 0; i < 20; i++) {
                ticket.sale();
            }
        }, "B").start();
        new Thread(() ->{
            for (int i = 0; i < 20; i++) {
                ticket.sale();
            }
        }, "C").start();

    }
}

//资源类
class Ticket {
    private int number = 10;
    public synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() +
                    "卖出了第" + (number--) + "票,剩余:" + number);
        }
    }
}

Lock接口

公平锁:十分公平:可以先来后到

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

锁是什么,如何判断锁的是谁?

有序执行A->B->C

public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(()->{
            for (int i = 0; i < 3; i++) {
                data.printA();
            }
        },"A").start();

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

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

class Data2 {
    private int munber = 0;

    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int number = 1;

    public void printA() {
        lock.lock();
        try {
            while (number != 1){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "-->AAAA");
            number = 2;
            //唤醒指定的人,B
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB() {
        lock.lock();
        try {
            while (number != 2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "-->BBBB");
            number = 3;
            //唤醒指定的人,B
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC() {
        lock.lock();
        try {
            //业务,判断-》执行-》通知
            while (number != 3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName() + "-->CCCC");
            number = 1;
            //唤醒指定的人,B
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

5 八锁问题

synchronized:该关键字的对象是方法的调用者!

两个方法用的是同一把锁,谁先拿到谁先执行。

new this 具体的一个对象

static Class唯一的一个模板

两个普通同步方法

/**
 * 1.标准情况下,两个线程先打印发短信还是打电话 answer:发短信
 * 2.sendSms延迟4秒,两个线程先打印发短信还是打电话 answer:发短信
 */
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        
        new Thread(() -> {
            phone.semdSms();
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {phone.call();}, "B").start();
    }
}

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

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

非同步方法和同步方法

/**
 * 3.增加了一个普通方法后,先执行发短信还是hello? answer: hello
 * 4.两个对象都执行同步方法 answer:打电话
 */
public class Test2 {
    public static void main(String[] args) {
        // 两个对象,两个调用者,两把锁
        Phone2 phone = new Phone2();
        Phone2 phone2 = new Phone2();

        new Thread(() -> {
            phone.semdSms();
        }, "A").start();

        new Thread(() -> {
            phone2.call();
        }, "D").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {phone.call();}, "B").start();

        new Thread(() -> {
            phone.hello();
        }, "C").start();
    }
}

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

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

    //hello 没有锁,不是同步锁,不受锁的影响
    public void hello() {
        System.out.println("hello");
    }
}

同步方法和静态同步方法

/**
 * 5.增加两个静态的同步方法,只有一个对象,先打电话还是发短信 answer 发短信
 * 6.两个对象,增加两个静态的同步方法。 answer 发短信
 */
public class Test3 {
    public static void main(String[] args) {
        // 两个对象,两个调用者,两把锁
        Phone3 phone = new Phone3();
        Phone3 phone3 = new Phone3();

        new Thread(() -> {
            phone.semdSms();
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//        问题5代码
//        new Thread(() -> {phone.call();}, "B").start();
        new Thread(() -> {phone3.call();}, "B").start();

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

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

Class对象和类对象

/**
 * 7. 一个静态的同步方法,1个普通的同步方法,一个对象,先打印谁? 打电话
 * 8. 一个静态的同步方法,一个普通的同步方法,两个对象,先打印谁? 打电话
 * @author Evan
 * @date 2021/3/18 15:06
 */
public class Test4 {
    public static void main(String[] args) {
        // 两个对象,两个调用者,两把锁
        Phone4 phone = new Phone4();
        Phone4 phone2 = new Phone4();

        new Thread(() -> {
            phone.semdSms();
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
//        问题7的代码
//        new Thread(() -> {phone.call();}, "B").start();
        new Thread(() -> {phone2.call();}, "B").start();

    }
}

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

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

}

6、集合类不安全

List不安全

使用ArrayList

public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        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();
        }
    }
}

输出:出现ConcurrentModificationException

说明:测试中出现过不报错的情况,循环次数增加到20次仍会出现完全不报错的情况。

[null, 1770e, b15b8, 38f61, 804a5]
[null, 1770e, b15b8, 38f61, 804a5, 620f7, 71db8]
[null, 1770e, b15b8, 38f61, 804a5, 620f7, 71db8, a8acf]
[null, 1770e, b15b8, 38f61, 804a5, 620f7, 71db8]
[null, 1770e, b15b8, 38f61, 804a5, 620f7]
[null, 1770e, b15b8, 38f61, 804a5]
[null, 1770e, b15b8, 38f61, 804a5]
[null, 1770e, b15b8, 38f61, 804a5]
[null, 1770e, b15b8, 38f61, 804a5]
Exception in thread "0" java.util.ConcurrentModificationException
public class ListTest {
    public static void main(String[] args) {
        /**
         * ArrayList 并发下会出现ConcurrentModificationException
         * 解决方案
         * 1.List<String> list = new Vector<>();
         * 2.List<String> list = Collections.synchronizedList(new ArrayList<>());
         * 3.List<String> list = new CopyOnWriteArrayList<>();
         */

        /**
         * CopyOnWrite 写入时复制, 这是计算机程序设计领域的一种优化策略
         * 多个线程调用的时候,list,读取的时候,固定的,写入覆盖
         * 在写入的时候避免覆盖,造成数据问题
         * 读写分离
         */
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 200; i++) {
            new Thread(() ->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

set不安全

HashSet底层是什么

//hash底层就是hashmap,根据map的key不重复的特性建立集合
public HashSet() {
    map = new HashMap<>();
}
// add方法
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
	// Dummy value to associate with an Object in the backing Map
	// 与背景map中的对象关联的虚拟值
   	private static final Object PRESENT = new Object();

hash底层就是hashmap,key不重复

public class SetTest {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0,4));
                System.out.println(set);
            }, String.valueOf(i).toString()).start();
        }
    }
}

解决方案

public class SetTest {
    public static void main(String[] args) {
        /**
         * Set<String> set = new HashSet<>(); 出现ConcurrentModificationException
         * 解决方案
         * 1.Set<String> set = Collections.synchronizedSet(new HashSet<>());
         * 2.Set<String> set = new CopyOnWriteArraySet<>();
         */

        Set<String> set = new HashSet<>();

        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0,4));
                System.out.println(set);
            }, String.valueOf(i).toString()).start();
        }
    }
}

Map不安全

public class MapSet {
    public static void main(String[] args) {

        /**
         * map默认等价于什么? new HashMap<>(16, 0.74) 和hashmap的原理有关
         * Map<String, String> map = new HashMap<>();
         * Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
         * Map<String, String> map = new ConcurrentHashMap<>();
         */
        Map<String, String> map = new HashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            }).start();
        }
    }
}

7、Callable

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        MyThread thread = new MyThread();
        FutureTask futureTask = new FutureTask(thread);

        new Thread(futureTask, "A").start();
        new Thread(futureTask, "B").start(); //结果会被缓存,效率高
        // 此时,FutureTask的state已非new状态,则此时会直接结束对应的线程,就会导致任务也不执行
        // 只是在第一次调用时返回结果保存了。(网友弹幕)
        //这个get方法可能会产生阻塞!把他放在最后
        Integer o = (Integer) futureTask.get();
		// 主线程会在此处等待,futureTask.get()返回结果后继续执行
        System.out.println(o);
        System.out.println("快点快点");
    }
}

class MyThread implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("call()"); //会打印几个call,只打印了一个
        TimeUnit.SECONDS.sleep(3);
        // 耗时的操作
        return 1024;
    }
}

输出

call()
1024
快点快点

8、常用辅助类

CountDownLatch

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        // 总数是6, 必须要执行任务的时候,再使用
        CountDownLatch countDownLatch = new CountDownLatch(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(); //等待计数器归零,然后再向下执行
        System.out.println("Close Door");
    }
}

输出

1Go out
5Go out
0Go out
4Go out
3Go out
2Go out
Close Door

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

CyclicBarrier

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        /**
         * 集齐7颗龙珠召唤神龙
         */
        // 召唤龙珠的线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("召唤神龙成功!");
        });

        for (int i = 0; i < 7; i++) {
            final int temp = i;
            //lambda表达式不能操作i,所以
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "收集" + temp + "个龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

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,然后唤醒等待的线程!

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

9、读写锁

ReadWriteLock

读可以被多个线程同时读,写的时候只能有一个线程去写

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

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

        //读取
        for (int i = 1; i <= 10; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.get(temp + "");
            }, String.valueOf(i)).start();
        }

    }
}

//加读写锁
class MyCacheLock{
    private final Map<String, Object> map = new HashMap<>();
    // 读写锁:更加细粒度的控制
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private final Lock lock = new ReentrantLock();
    // 存
    public void put(String key, Object value) {
        // 写锁
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "写入" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入ok");
        } finally {
            readWriteLock.writeLock().unlock();
        }

    }

    public void get(String key) {
        //实测读写10个线程不加锁,出现写入过程中读取,因此要加锁
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "读取" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取ok");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}
//不加锁
class MyCache{
    private volatile Map<String, Object> map = new HashMap<>();

    public void put(String key, Object value) {
        System.out.println(Thread.currentThread().getName() + "写入" + key);
        map.put(key, value);
        System.out.println(Thread.currentThread().getName() + "写入ok");
    }

    public void get(String key) {
        System.out.println(Thread.currentThread().getName() + "读取" + key);
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName() + "读取ok");
    }
}

10、阻塞队列

阻塞队列的使用场景:多线程并发处理,线程池!

四组API

.put 队列没有位置了,会一直阻塞

11、线程池

池化技术

线程池的好处

1、降低资源的消耗

2、提高响应的速度

3、方便管理

线程复用、

12、四大函数式接口

新时代的程序员:

lambda表达式

链式编程

函数式接口

Stream流计算

16、 JMM

请你谈谈Volatile的理解

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

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

什么是JMM

Java内存模型,不存在的东西,是概念和约定

关于JMM的一些同步的约定

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

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

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

线程 工作内存,主内存

public class JMMDemo {
    private static int num =0;

    public static void main(String[] args) {
        new Thread(() -> {
            while (num == 0) {

            }
        }).start();

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

设想的是num在主线程中赋值为1,new线程也因为num != 0而停止运行。实际运行时,new线程未停止。这是因为主内存中的num==1,而线程的工作内存未做相应的修改,因此不会停止运行。
解决办法见下一节。

17、Volatile

1、保证可见性

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

            }
        }).start();

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

2、不保证原子性

原子性:不可分割

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

public class VDemo2 {
    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);
    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JdEcQga1-1616655476952)(…/image/image-20210322113602501.png)]

输出始终是20000,未出现视频中的小于20000的情况。但是idea会有警告。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LxYhg3wR-1616655476954)(…/image/image-20210322113722836.png)]

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

public class VDemo3 {
    // 原子类的 Integer
    private volatile static AtomicInteger num = new AtomicInteger();
    public static void add() {
        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);
    }
}

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

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

单例模式

搞懂单例模式和枚举

)]

输出始终是20000,未出现视频中的小于20000的情况。但是idea会有警告。

[外链图片转存中…(img-LxYhg3wR-1616655476954)]

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

public class VDemo3 {
    // 原子类的 Integer
    private volatile static AtomicInteger num = new AtomicInteger();
    public static void add() {
        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);
    }
}

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

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

单例模式

搞懂单例模式和枚举

构造器私有

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值