JUC并发编程详解

1、什么是JUC?

java.util.concurrnet

java.util.concurrent.atomic

java.util.concurrent.locks

2、进程和线程

进程:一个程序的实例

线程:是一个进程的实体,CPU调度和分配的基本单位

java默认有两个线程,main方法和GC

开启线程的方式:Thread、Runnable、Callable

Java本质是无法开启线程的,是调用本地方法(c++)来开启线程的,java无法操作硬件。

3、并发、并行

并发编程:并发、并行

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

并行:多个线程同时进行

public static void main(String[] args) {

int count = Runtime.getRuntime().availableProcessors();

System.out.println("CPU处理器数量:" + count); // 检测cpu逻辑处理器数量

}

并发编程的本质:充分利用CPU的资源

线程的六种状态

public enum State {

// 新生

NEW,

// 运行

RUNNABLE,

// 阻塞

BLOCKED,

// 等待

WAITING,

// 超时等待

TIMED_WAITING,

// 终止

TERMINATED;

}

wait和sleep的区别

属于不同类

wait属于java.lang.Object类

sleep属于

java.util.concurrent.TimeUnit类

关于锁的释放

wait会释放锁

sleep不会释放锁

使用范围不同

wait必须在同步代码块使用

sleep可以在任意地方使用

是否需要捕获异常

wait不需要捕获异常

sleep必须需要捕获异常

synchronized 和 Lock 的区别

synchronized | Lock

java的一个关键字 | 一个接口,有很多实现类

无法判断锁的状态 | 可以判断是否获取了锁

可以自动释放锁 | 只能手动在finally中释放锁,否则会死锁

假设A线程获取锁的时候,B线程等待,如果A线程阻塞了,那么B线程智能永远等待 Lock可以尝试获取锁,有多种获取锁的方式 |

synchronized是可重入锁,非公平锁,不可以中断 Lock是可重入锁,默认是非公平锁(可以设置成公平锁),可以中断

功能单一,适合锁少量同步代码 | API丰富,灵活度高,适合锁大量同步代码

Callable

callable是创建线程的第三种方式

与之前两种的区别:

有返回值

可以抛出异常

方法与之前不同,之前是run(),现在是call()

public class CallableTest {

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

MyThread myThread = new MyThread();

FutureTask futureTask = new FutureTask(myThread);

new Thread(futureTask, "A").start();

new Thread(futureTask, "B").start(); // 有缓存,第二次不会再次输出

Integer result = (Integer) futureTask.get(); // 这个get方法,可能会产生阻塞,把他放在最后,或者异步调用

System.out.println(result);

}

}

class MyThread implements Callable {

@Override

public Integer call() throws Exception {

return 1234;

}

}

常用辅助类

CountDownLatch(减法计数器)

// 减法计数器

public class CountDownLatchDemo {

public static void main(String[] args) throws InterruptedException {

// 假设有一个任务六个线程必须执行,全部执行完成后才能继续执行后面的代码

CountDownLatch countDownLatch = new CountDownLatch(6);

for (int i = 1; i <= 6; i++) {

new Thread(() -> {

System.out.println(Thread.currentThread().getName() + "执行成功~");

countDownLatch.countDown(); // 数量-1

}, String.valueOf(i)).start();

}

countDownLatch.await(); //等待计数器归零,再继续向下执行

System.out.println("六个线程全部执行成功!");

}

}

CyclicBarrier(加法计数器)

// 加法计数器

public class CyclicBarrierDemo {

public static void main(String[] args) {

// 集齐7颗龙珠召唤神龙

CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {

System.out.println("7颗龙珠已集齐,召唤神龙~");

});

for (int i = 1; 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();

}

}, String.valueOf(i)).start();

}

}

}

Semaphore(计数信号量)

// 计数信号量

public class SemaphoreDemo {

public static void main(String[] args) {

// 假设有3个车位,6个车去停

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();

}

}

}

读写锁ReadWriteLock

ReadWriteLock维护了一堆关联的locks,一个只用于读,一个用于写,读的时候可以多线程去读,写的时候只能一个线程去写

也被称作:独占锁和共享锁

public class ReadWriteLockDemo {

public static void main(String[] args) {

// 5个线程同时写入自定义缓存,要求一个一个写,防止写入错误,五个线程同时读缓存,读的顺序任意

MyCache cache = new MyCache();

// 5个线程同时写

for (int i = 1; i <= 5; i++) {

final int temp = i;

new Thread(() -> {

cache.put(temp + "", temp + "");

}, String.valueOf(i)).start();

}

// 5个线程同时读取

for (int i = 1; i <= 5; i++) {

final int temp = i;

new Thread(() -> {

cache.get(temp + "");

}, String.valueOf(i)).start();

}

}

}

class MyCache {

private volatile Map map = new HashMap<>();

private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); // 读写锁

// 存,写入

public void put(String key, Object value) {

readWriteLock.writeLock().lock(); // 写锁加锁

try {

System.out.println(Thread.currentThread().getName() + "正在写入,Key:" + key);

map.put(key, value); // 写入

System.out.println(Thread.currentThread().getName() + "写入成功");

} catch (Exception e) {

e.printStackTrace();

} finally {

readWriteLock.writeLock().unlock(); // 写锁释放

}

}

// 取,读取

public void get(String key) {

readWriteLock.readLock().lock(); // 读锁加锁

try {

System.out.println(Thread.currentThread().getName() + "正在读取,Key" + key);

map.get(key); // 读取

System.out.println(Thread.currentThread().getName() + "读取成功");

} catch (Exception e) {

e.printStackTrace();

} finally {

readWriteLock.readLock().unlock(); // 读锁释放

}

}

}

阻塞队列

什么时候阻塞?

写入时队列是满的会产生阻塞

读取时队列为空时会产生阻塞

BlockingQueue:阻塞队列

BlockingDueue:阻塞双端队列

抛出异常方式

/**

* 抛出异常方式

*/

public static void test1() {

ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3); // 队列大小为3

System.out.println(queue.add("a"));

System.out.println(queue.add("b"));

System.out.println(queue.add("c"));

System.out.println(queue.element()); // 检测队列首部元素

// System.out.println(queue.add("d")); // 插入数据大于队列长度抛出异常:

java.lang.IllegalStateException: Queue full 队列已满

System.out.println(queue.remove());

System.out.println(queue.remove());

System.out.println(queue.remove());

// System.out.println(queue.remove()); // 队列中没有数据,再次移除会抛出异常:

java.util.NoSuchElementException 没有元素异常

}

当添加的数据大于队列大小时会抛出异常:

java.lang.IllegalStateException: Queue full 队列已满

当移除数据时队列为空时会抛出异常:

java.util.NoSuchElementException 没有元素异常

有返回值,不抛出异常

/**

* 有返回值,不抛出异常

*/

public static void test2() {

ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3);

System.out.println(queue.offer("a")); // 返回值为true

System.out.println(queue.offer("b"));

System.out.println(queue.offer("c"));

System.out.println(queue.element()); // 检测队列首部元素

// System.out.println(queue.offer("d")); // 超出队列长度,返回值为false

System.out.println(queue.poll());

System.out.println(queue.poll());

System.out.println(queue.poll());

// System.out.println(queue.poll()); // 取出时队列为空,返回值为null

}

当添加数据大于队列大小时,不抛出异常,返回值为false

当移除数据队列为空时,不抛出异常,返回值为null

等待,阻塞(一直等待)

/**

* 等待,阻塞(一直等待)

*/

public static void test3() throws InterruptedException {

ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3);

queue.put("a"); // 无返回值

queue.put("b");

queue.put("c");

// queue.put("d"); // 超出队列长度,会一直等待队列有位置再添加到队列

System.out.println(queue.take());

System.out.println(queue.take());

System.out.println(queue.take());

// System.out.println(queue.take()); // 队列为空时一会一直等待队列中有数据加入再取出

}

等待,阻塞(超时退出)

/**

* 等待,阻塞(超时退出)

*/

public static void test4() throws InterruptedException {

ArrayBlockingQueue queue = new ArrayBlockingQueue<>(3);

System.out.println(queue.offer("a"));

System.out.println(queue.offer("b"));

System.out.println(queue.offer("c"));

// System.out.println(queue.offer("d", 2, TimeUnit.SECONDS)); // 如果队列满了,等待两秒,超过两秒后退出,返回false

System.out.println(queue.poll());

System.out.println(queue.poll());

System.out.println(queue.poll());

// System.out.println(queue.poll(2, TimeUnit.SECONDS)); // 如果队列为空,等待两秒,超过两秒后退出,返回null

}

SynchronousQueue 同步队列

同步队列没有容量,put进去了一个元素,必须take出来,才能继续使用

public class SynchronousQueueDemo {

public static void main(String[] args) {

BlockingQueue queue = new SynchronousQueue<>();

new Thread(() -> {

try {

System.out.println(Thread.currentThread().getName() + " put a");

queue.put("a");

System.out.println(Thread.currentThread().getName() + " put b");

queue.put("b");

System.out.println(Thread.currentThread().getName() + " put c");

queue.put("c");

} catch (InterruptedException e) {

e.printStackTrace();

}

}, "T1").start();

new Thread(() -> {

try {

TimeUnit.SECONDS.sleep(2);

System.out.println(Thread.currentThread().getName() + " take " + queue.take());

TimeUnit.SECONDS.sleep(2);

System.out.println(Thread.currentThread().getName() + " take " + queue.take());

TimeUnit.SECONDS.sleep(2);

System.out.println(Thread.currentThread().getName() + " take " + queue.take());

} catch (InterruptedException e) {

e.printStackTrace();

}

}, "T2").start();

}

}

你可能感兴趣的:(JUC并发编程详解)