JUC并发编程知识点,虽然凌乱,但也只为唤起记忆(一)

1. 零零碎碎的知识开篇

Java程序默认有两个线程,一个main一个GC(垃圾回收的守护线程)

Java能自己开线程吗???
其实是不能的,java开启线程调用的native start0()方法,由C++开线程。Java程序是运行在JVM上的,不能直接操作硬件。

2. 线程的六种状态

  1. NEW
  2. RUNNABLE
  3. BLOCKED
  4. WAITING
  5. TIMED_WAITING
  6. TERMINATED

3. wait() 和 sleep()的区别

  1. 原理不同。wait()是Object类的方法,用于线程通信。sleep()是Thread的静态方法,用于控制流程,让线程睡觉,把执行的机会让给其他线程。
  2. 使用的区域不同。wait()用于同步方法或同步代码块儿中。sleep()随处都可睡。
  3. 对锁的处理也不同。wait()的时候就会把锁给释放了,而sleep()则会和锁一起睡觉,不会释放锁。

4. synchronized 和 Lock 的区别

  1. synchronized 是内置的关键字;Lock是java的接口
  2. synchronized 无法判断锁的状态;Lock可以判断锁的状态
  3. synchronized 会自动释放锁;Lock需要手动释放锁
  4. synchronized 会当线程1获得锁并且阻塞的话,线程2会一直等待;Lock不一定会一直等待下去,可以尝试获得锁,获取不到,就不再等待
  5. synchronized 可重入,不可中断,非公平;Lock 可重入,可判断,可公平
  6. synchronized 适合少量代码的同步问题;Lock适合大量同步代码的问题

记忆过程:根据Lock的使用过程,先创建锁(可以想到1,5),加锁释放锁(想到2,3),46点再使劲记一下吧。。。

5. 生产者消费者问题

这个知识点主要需要理解,线程之间的通信,每个线程都有自己的等待条件,执行业务,再通知其他线程,我愿称之,《等待,业务,唤醒》
synchronized 用的是 wait() 和notifyall()
Lock 用的是 Condition 下的 await()和signalAll() (注:signal() 也有妙用,在多个Condition时,可用于定向的唤醒)

重要的要记住:在对等待条件进行判断的时候,用的时while()而不是if()
官方文档中说,用while()来防止虚假唤醒问题,因为while()能一直检查条件,而if()只检查一次

public class ProducerAndConsumer01 {

    //在两个线程下,正常用if判断等待的条件是可以做到的
    //但是若增加到多个线程,则会出现异常情况。
    //此时if应该改为while,while是多次判断,if是单次判断,所以if会有虚假唤醒的问题
    //就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,
    //但是不会重新判断if条件,直接继续运行if代码块之后的代码,
    //而如果使用while的话,也会从wait之后的代码运行,
    //但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。

    //生产者和消费者问题 等待,业务,唤醒
    public static void main(String[] args) {
        Item item = new Item();

        new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    item.increase();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    item.decrease();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B").start();


        new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    item.increase();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"C").start();

        new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    item.decrease();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"D").start();
    }
}

class Item{

    private int num = 0;

    public synchronized void increase() throws InterruptedException {
        while (num != 0){
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName() + "->" + num);
        this.notifyAll();
    }

    public synchronized void decrease() throws InterruptedException {
        while(num == 0){
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "->" + num);
        this.notifyAll();

    }
}
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.printA();
            }

        }).start();

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

        }).start();

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

class Data03{
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();

    private int num = 0;

    public void printA(){
        lock.lock();
        try {
            while (num != 0){
                //等待
                condition1.await();
            }
            //业务
            System.out.println("大家好,正在打印AAA");
            num++;
            condition2.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB(){
        lock.lock();
        try {
            while (num != 1){
                //等待
                condition2.await();
            }
            //处理业务
            System.out.println("大噶好,正在打印BBB");
            num++;
            //唤醒
            condition3.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC(){
        lock.lock();

        try {
            while (num != 2){
                //等待
                condition3.await();
            }
            //处理业务
            System.out.println("雷猴啊,正在打印CCC");
            num = 0;
            //唤醒
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

6. 8锁问题

  1. 针对synchronized,不同的对象,锁不同,执行同步方法时,不互相干扰。
  2. 针对static synchronized,锁住的是类,区别于非静态的锁,这两个锁互相不干扰
  3. 普通方法不涉及锁的问题,执行不受影响

7. 线程不安全的集合

  1. List接口下的集合都不安全,解决方法如下
List<String> list = Collections.synchronizedList(new ArrayList<String>());

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

//还有一个过时的Vector也可以解决,Vector add方法是利用的synchronized同步的
  1. 解决Set的线程安全问题
Set<String> set = Collections.synchronizedSet(new HashSet<String>());

CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();

HashSet的底层是HashMap,调用的add方法是HashMap的put方法,HashSet利用的是HashMap中key不能重复的性质

  1. 解决Map的线程不安全问题
Map<String,String> map = Collections.synchronizedMap(new HashMap<>());

ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();

CopyOnWrite 是计算机程序设计的一种优化策略
在写入时复制,避免重复

8. 实现Callable接口执行多线程方法

  • Runnable和Callable的区别
  1. 它们都是函数式接口
  2. Callable有返回值,Runnable没有
  3. Callable方法是call,Runable是run

泛型的类型就是返回值类型
FatureTask实现了Runnable接口,而FatureTask的构造器中需要传入Callable的对象,这样,将FatureTask传入Thread时,便将Callable和Thread勾搭了起来
利用FatureTask的get方法获取返回值,可能发生阻塞,而且还会被缓存

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Call {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread myThread = new MyThread();

        FutureTask futureTask = new FutureTask(myThread);

        new Thread(futureTask).start();

        System.out.println(futureTask.get());
    }
}
class MyThread implements Callable<String>{

    @Override
    public String call() throws Exception {
        System.out.println("亲爱哒,这是call()方法哦");
        return "好哒,俺知道了";
    }
}

9. 常用的辅助类

public class CountDownLatch {
    public static void main(String[] args) throws InterruptedException {
        java.util.concurrent.CountDownLatch countDownLatch = new java.util.concurrent.CountDownLatch(3);

        for (int i = 0; i < 3; i++) {
            new Thread(()->{
                //相当于是 -1 操作
                countDownLatch.countDown();
                System.out.println(Thread.currentThread().getName() + " :我减!");
            }).start();
        }

        //这里是在计数器减到0的时候,才能唤醒,否则一直等着
        countDownLatch.await();

        System.out.println("啦啦啦,看来这个CountDownLatch,你了解一点儿了");

    }
}
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class Cyclicbarrier {
    public static void main(String[] args) {
        //构造函数中,有7,到7召唤神龙
        //7的意思是,你创建了7个线程才能执行
        //到7的时候会自动唤醒 cyclicBarrier 的 await
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("召唤神龙成功");
        });

        for (int i = 0; i < 7; i++) {
            int temp = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "拿到了第" + temp + "颗龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphorE {
    public static void main(String[] args) {
        //3个信号量,require就减一个,release就加一个
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 9; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "占用了信号");
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName() + "释放了信号");
                }
            }).start();
        }
    }
}
原创文章 34 获赞 8 访问量 1155

猜你喜欢

转载自blog.csdn.net/qq_46225886/article/details/105757206