JUC并发编程 - ZZyy

JUC并发编程 - ZZyy

什么是JUC

JDK1.5出现的,用来处理线程的工具包

进程与线程

进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程一 -资源分配的最小单位。
线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程一程序执行的最小单位。

线程的状态

NEW(新建)、RUNNABLE(可运行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(定时等待)、TERMINATED(终止)

wait和sleep区别

①sleep是Thread的静态方法,wait是Object的方法任何对象实例都能调用
②sleep不会释放锁也不会占用锁。wait会释放锁,但调用前提是当前线程有锁
③都可以被interrupted方法中断

并发与并行

解释一:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。
解释二:并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
解释三:在一台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。


并发:指的是多个事情,在同一时间段内同时发生了,多个任务之间是互相抢占资源的。
并行:指的是多个事情,在同一时间点上同时发生了,多个任务之间是不互相抢占资源的。

只有在多CPU的情况中,才会发生并行。否则,看似同时发生的事情,其实都是并发执行的。

Lock 和 Synchronized

①Synchronized是Java关键字,是同步锁。可修饰代码块、方法(不被子类继承)、静态方法、类
②发生异常会自动释放锁,如果被阻塞会一直无限期等待
③synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现为以下3种形式。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的class对象。
对于同步方法块,锁是Synchonized括号里配置的对象。


①Lock是一个接口,通过这个接口的实现类可以实现同步访问,必须要用户手动释放锁,如果不释放会发生死锁。
②Lock可以让等待锁的线程响应中断,Synchronized却不行,等待线程会一直等待下去
③Lock可以知道有没有成功获取锁
④Lock可以提高多个线程进行读操作的效率

线程间的通信 等待/通知

①关键字Synchronized通过wait()/notify()两个方法实现等待/通知。
②Lock锁的newCondition()方法返回Condition对象,Condition类也可以实现等待/通知。

区别:
notify()随机唤醒,使用Condition可以选择性通知,condition比较常用的两个方法:
• await()会使当前线程等待,同时会释放锁,当其他线程调用 signal()时,线程会重新获得锁并继续执行。
• signal()用于唤醒一个等待的线程。

Lock 和 ReadWriteLock 接口

Lock的实现类ReentrantLock可重入锁
ReadWriteLock的实现类ReentrantReadWriteLock可重入读写锁,读写锁分开,从而使得线程可以同时进行读操作
注意:
有一个线程占用读锁,其他线程申请写锁会等待读锁释放
有一个线程占用写锁,其它线程申请写锁或读锁会一直等待释放写锁

集合的线程安全

Vector

所有的方法,都是通过synchronized修饰,方法锁,实际也是对象锁,安全

Collections

Collections 提供了方法 synchronizedList 保证 list 是同步线程安全的
都是通过mutex对象加锁,一样,效率和Vector一样低

CopyOnWriteArrayList(重点)

写时复制的方法,来控制的读写分离。但是存在的问题就是,会出现脏读的现象

Callable&Future 接口

创建线程的方法,一种是通过Threa类,另一种使用Runnable创建线程,无法使线程返回结果,而Callable可以。

Callable接口

Runnable只需要实现不返回任何内容的run()方法,对于Callable需要实现完成时返回结果的call()方法。
call()方法可以引发异常,而run()不能
new Thread(Runnable r)不能直接替换Runnable,因为Thread类构造方法没有Callable

Future接口 实现类 FutureTask

当 call()方法完成时,结果必须存储在主线程已知的对象中,以便主线程可以知道该线程返回的结果。为此,可以使用 Future 对象。

  • public boolean cancel(boolean mayInterrupt):用于停止任务
  • public Object get() 抛出 InterruptedException,ExecutionException:用于获取任务的结果。
  • public boolean isDone():如果任务完成,则返回 true,否则返回 false

在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给 Future 对象在后台完成
一般 FutureTask 多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果
get 方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常

public class CallableDemo {

    static class Mythread1 implements Runnable{
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName()+"线程进入了run方法");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    static class Mythread2 implements Callable{

        @Override
        public Long call() throws Exception {
            try {
                System.out.println(Thread.currentThread().getName()+"线程进入了call方法,开始准备睡觉");
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+"睡醒了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return System.currentTimeMillis();
        }
    }

    public static void main(String[] args) throws Exception {
        Runnable runnable = new Mythread1();
        Callable callable = new Mythread2();
        FutureTask<Long> futureTask = new FutureTask<Long>(callable);
        //线程一
        new Thread(runnable,"线程一").start();
        //线程二
        new Thread(futureTask,"线程二").start();
        for (int i = 0; i < 10; i++) {
            Long result = futureTask.get(); // 阻塞等待计算结果
            System.out.println(result);
        }
    }

}

JUC辅助类

  • CountDownLatch 一个或者一组线程在开始执行操作之前,必须要等到其他线程执行完才可以
public class CountDownLatchDemo {

    public static void main(String[] args) throws Exception {
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                try {
                    if (Thread.currentThread().getName().equals("同学6")){
                        Thread.sleep(2000);
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName()+"离开了");
                countDownLatch.countDown();
            },"同学"+i).start();
        }
        System.out.println("主线程睡觉");
        countDownLatch.await();
        System.out.println("全部离开了,现在的计数器为"+countDownLatch.getCount());
    }
}
  • CyclicBarrier 循环屏障
    CyclicBarrier 的构造方法第一个参数是目标障碍数,每次执行 CyclicBarrier 一次障碍数会加一,如果达到了目标障碍数,才会执行 cyclicBarrier.await()之后的语句。
public class CyclicBarrierDemo {
    private final static int NUMBER = 7;

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
            System.out.println("集齐" + NUMBER + "颗龙珠,现在召唤神龙!!!!!!!!!");
        });

        for (int i = 1; i <= 7; i++) {
            new Thread(() -> {
                try {
                    if (Thread.currentThread().getName().equals("龙珠3号")) {
                        System.out.println("龙珠3号抢夺战开始,悟空变身超级赛亚人");
                        Thread.sleep(5000);
                        System.out.println("龙珠3号抢夺战结束,悟空赢了,拿到了龙珠3号");
                    } else {
                        System.out.println(Thread.currentThread().getName() + "收集到了!");
                    }
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "龙珠" + i + "号").start();
        }


    }

}

原文地址:https://www.cnblogs.com/ZZyy-/archive/2022/12/05/16952208.html