【JavaSE】之JUC并发编程(上)


前言

本文为JUC并发编程相关知识,Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~

一、JUC概述

JUC指的是Java的并发工具包,包含以下三个包:

  • java.util.concurrent
  • java.util.concurrent.atomic:支持原子型操作
  • java.util.concurrent.locks:lock锁

二、进程与线程

1.进程

  • 程序执行的一次过程,一个进程包含一个或多个线程。进程是资源分配的单位。

2.线程

  • 可以指程序执行过程中,负责实现某个功能的单位。线程是CPU调度和执行的单位。

3.并发

  • 同一时刻,多个线程交替执行。(一个CPU交替执行线程)。

4.并行

  • 同一时刻,多个线程同时执行。(多个CPU同时执行多个线程)。
# 获取cpu的核数(cpu密集型;io密集型)
System.out.println(Runtime.getRuntime().availableProcessors());

5.并发编程的本质

  • 并发编程的本质是充分利用cpu资源。

三、多线程回顾

1.线程的几种状态

    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
         // 新生
        NEW,
        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
         // 运行
        RUNNABLE,
        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
         // 阻塞
        BLOCKED,
        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
         // 等待,死死的等待
        WAITING,
        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
         // 超时等待
        TIMED_WAITING,
        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
         // 终止
        TERMINATED;
    }

2.sleep和wait的区别

  • sleep是Thread类的本地方法;wait是Object类的方法。
  • sleep不释放锁;wait释放锁。
  • sleep不需要和synchronized关键字一起使用;wait必须和synchronized代码块一起使用。
  • sleep不需要被唤醒(时间到了自动退出阻塞);wait需要被唤醒。
  • sleep一般用于当前线程休眠,或者轮循暂停操作;wait则多用于多线程之间的通信。

四、Lock锁

1.传统的synchronized

synchronized中文意思是同步,也称之为”同步锁“。
synchronized的作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果。
synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。
在JDK1.5之前synchronized是一个重量级锁,相对于j.u.c.Lock,它会显得那么笨重,随着Javs SE 1.6对synchronized进行的各种优化后,synchronized并不会显得那么重了。

synchronized的作用主要有三个:

  • 原子性:确保线程互斥地访问同步代码;
  • 可见性:保证共享变量的修改能够及时可见,其实是通过Java内存模型中的“对一个变量unlock操作之前,必须要同步到主内存中;如果对一个变量进行lock操作,则将会清空工作内存中此变量的值,在执行引擎使用此变量前,需要重新从主内存中load操作或assign操作初始化变量值”来保证的;
  • 有序性:有效解决重排序问题,即 “一个unlock操作先行发生(happen-before)于后面对同一个锁的lock操作”;

2.公平锁和非公平锁(锁的底层)

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

3.Lock锁

Lock锁是一个接口,他有三个实现类:

  • ReentrantLock类
  • ReentrantReadWriteLock.ReadLock
  • ReentrantReadWriteLock.WriteLock

4.Lock锁和synchronized的区别

  • Synchronized是内置Java关键字;Lock是一个Java类。
  • Synchronized无法判断获取锁的状态;Lock可以判断是否获取到了锁。(boolean b = lock.tryLock();)
  • Synchronized会自动释放锁;Lock必须要手动释放锁,如果不释放锁,死锁。
  • Synchronized线程1获得锁阻塞时,线程2会一直等待下去;Lock锁线程1获得锁阻塞时,线程2等待足够长的时间后中断等待,去做其他的事。
  • Synchronized可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置)。
    lock.lockInterruptibly();方法:当两个线程同时通过该方法想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
  • Synchronized适合锁少量的代码同步问题;Lock适合锁大量的同步代码。

5.生产消费者

生产者和消费者问题:synchronized版

public class Demo04 {
 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();
 }
}
// 判断等待,业务,通知
class Data {
 private int i = 0;
 // +1
 public synchronized void increment() throws InterruptedException {
     if (i != 0) {
         this.wait();
     }
     i++;
     System.out.println(Thread.currentThread().getName() + "=>" + i);
     // 通知其他线程我+1完成
     this.notifyAll();
 }
 // -1
 public synchronized void decrement() throws InterruptedException {
     if (i==0){
         this.wait();
     }
     i--;
     System.out.println(Thread.currentThread().getName() + "=>" + i);
     // 通知其他线程,我-1完毕
     this.notifyAll();
 }
}

问题存在:A、B、C、D四个线程!虚假唤醒问题
if改成while解决虚假唤醒

 public class Demo04 {
     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 int i = 0;
     // +1
     public synchronized void increment() throws InterruptedException {
         while (i != 0) {
             this.wait();
         }
         i++;
         System.out.println(Thread.currentThread().getName() + "=>" + i);
         // 通知其他线程我+1完成
         this.notifyAll();
     }
     // -1
     public synchronized void decrement() throws InterruptedException {
         while (i==0){
             this.wait();
         }
         i--;
         System.out.println(Thread.currentThread().getName() + "=>" + i);
         // 通知其他线程,我-1完毕
         this.notifyAll();
     }
 }

生产者和消费者问题:JUC版

public class Demo04 {
 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 int i = 0;
 Lock lock = new ReentrantLock();
 Condition condition = lock.newCondition();
 // +1
 public  void increment() throws InterruptedException {
     lock.lock();
     try {
         while (i != 0) {
             condition.await();
         }
         i++;
         System.out.println(Thread.currentThread().getName() + "=>" + i);
         // 通知其他线程我+1完成
         condition.signalAll();
     } catch (InterruptedException e) {
         e.printStackTrace();
     } finally {
         lock.unlock();
     }
 }
 // -1
 public void decrement() throws InterruptedException {
     lock.lock();
     try {
         while (i==0){
             condition.await();
         }
         i--;
         System.out.println(Thread.currentThread().getName() + "=>" + i);
         // 通知其他线程,我-1完毕
         condition.signalAll();
     } catch (InterruptedException e) {
         e.printStackTrace();
     } finally {
         lock.unlock();
     }
 }
}

6.Condition实现精准通知唤醒

public class Demo05 {
    public static void main(String[] args) {
        Data01 data01 = new Data01();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data01.A();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data01.B();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data01.C();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
    }
}
// 判断等待,业务,通知
//A执行完调用B,B执行完调用C,C执行完调用A
class Data01 {
    private int num = 1;// 1A 2B 3C
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    public void A() throws InterruptedException {
        lock.lock();
        try {
            // 业务代码,判断=>执行=>通知!
            while (num!=1){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>AAAAA");
            num = 2;
            // 唤醒指定的线程,B
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void B() throws InterruptedException {
        lock.lock();
        try {
            while (num!=2){
                condition2.await();
            }
            num = 3;
            System.out.println(Thread.currentThread().getName()+"=>BBBBB");
            // 唤醒指定的线程,C
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void C() throws InterruptedException {
        lock.lock();
        try {
            while (num!=3){
                condition3.await();
            }
            num = 1;
            System.out.println(Thread.currentThread().getName()+"=>CCCCC");
            // 唤醒指定的线程,A
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

五、8锁现象

六、不安全集合类

1.ArryList集合

多线程下不安全;可能会报错:java.util.ConcurrentModificationException(并发修改异常)

// java.util.ConcurrentModificationException:并发修改异常
public class Test11 {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                strings.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(strings);
            }).start();
        }
    }
}

解决方案:

  • List list = new Vector<>();
  • List strings = Collections.synchronizedList(new
    ArrayList<>());
  • List strings = new CopyOnWriteArrayList<>();

2.HashSet集合

HashSet集合的底层是hashmap的key;多线程下不安全;可能会报错:java.util.ConcurrentModificationException(并发修改异常)

// java.util.ConcurrentModificationException:并发修改异常
public class Test11 {
    public static void main(String[] args) {
        // Set<String> strings = Collections.synchronizedSet(new HashSet<>());
        HashSet<String> strings = new HashSet<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                strings.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(strings);
            }).start();
        }
    }
}

解决方案:

  • Set strings = Collections.synchronizedSet(new HashSet<>());
  • Set strings = new CopyOnWriteArraySet<>();

3.HashMap集合

多线程下不安全;可能会报错:java.util.ConcurrentModificationException(并发修改异常)

// java.util.ConcurrentModificationException:并发修改异常
public class Test11 {
    public static void main(String[] args) {
        // 默认相当于
        Map<String, String> map = new HashMap<>(16, 0.75F);
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            }).start();
        }
    }
}

解决方案:

  • 使用Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();

七、Callable接口

Callable接口类似于Runnable接口,是线程第三种创建方式,有以下特点:

  • 可以抛出异常。
  • 可以有返回值。
  • 方法不同与Runnable接口。用的是Call方法。
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask(new MyThread());// 适配类
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();// 打印一个Call,结果会被缓存,提高效率
        Integer s = (Integer) futureTask.get();// get方法可能会产生阻塞
        System.out.println(s);
    }
}
class MyThread implements Callable<Integer>{
    @Override
    public Integer call(){
        System.out.println("Call");
        return 1024;
    }
}

八、常用辅助类

1.CountDownLatch

应用场景:

  • 多线程任务汇总。
  • 多线程任务阻塞住,等待发令枪响,一起执行。

每次有线程调用,数量-1,当计数器归零,countDownLatch.await()就会被唤醒向下执行。

// 计数器
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
            }).start();
        }
        countDownLatch.await();// 等待计数器归零,然后再往下执行
        System.out.println("关门");
    }
}

// 打印:
// Thread-0=>Go Out
// Thread-5=>Go Out
// Thread-4=>Go Out
// Thread-2=>Go Out
// Thread-3=>Go Out
// Thread-1=>Go Out
// 关门

2.CyclicBarrier

应用场景:允许一组线程全部等待彼此达到共同屏障点的同步辅助。

// 相当于加法计数器
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        // 集齐七颗龙珠召唤神龙
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {// 如果计数器为7,线程只有6个,则会等待,不进行召唤神龙
            System.out.println("召唤神龙");
        });
        for (int i = 0; i < 7; i++) {
            final 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();
        }
    }
}

3.Semaphore

Semaphore:信号量

public class SemaphoreDemo {
    public static void main(String[] args) {
        // 线程数量:停车位!限流
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; 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();// 释放
                }
            }).start();
        }
    }
}

// 打印:
// Thread-0抢到车位!
// Thread-2抢到车位!
// Thread-1抢到车位!
// Thread-2离开车位!
// Thread-1离开车位!
// Thread-0离开车位!
// Thread-3抢到车位!
// Thread-5抢到车位!
// Thread-4抢到车位!
// Thread-5离开车位!
// Thread-4离开车位!
// Thread-3离开车位!

原理:

  • semaphore.acquire();获得,假设已经满了则等待,等待其他线程释放。
  • semaphore.release();释放,会将当前的信号量释放+1,然后唤醒等待的线程。

九、读写锁

  • ReadWriteLock接口有一个实现类ReentrantReadWriteLock类。
  • 读可以被多个线程同时读,写的时候只能有一个线程去写。
/**
 * 独占锁(写锁):一次只能被一个线程占有
 * 共享锁(读锁):多个线程可以同时占有
 * ReentrantLock:
 * 读-读:可以共存
 * 读-写:不可以共存
 * 写-写:不可以共存
 */
public class ReentrantLockDemo {
    public static void main(String[] args) {
        MyCacheLock myCache = new MyCacheLock();
        // 5个线程写
        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.put(temp + "", temp + "");
            }, String.valueOf(i)).start();
        }
        // 5个线程读
        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.get(temp + "");
            }, String.valueOf(i)).start();
        }
    }
}
class MyCacheLock {
    private volatile Map<String, Object> map = new HashMap<>();
    // 读写锁,更加细粒度的控制
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // 写,同时只有一个线程写
    public void put(String key, Object obj) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "写入");
            map.put(key, obj);
            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() + "读取");
            map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

后记

Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小新要变强

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值