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

简介: 【JavaSE】之JUC并发编程(上)(二)

五、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全栈学习技术清单~

相关文章
|
Java 调度 Maven
JUC并发编程【java提高】1
JUC并发编程【java提高】1
98 0
|
2月前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
129 6
【Java学习】多线程&JUC万字超详解
|
Java 调度 数据安全/隐私保护
JUC并发编程【java提高】4
JUC并发编程【java提高】4
58 0
|
SQL 存储 缓存
JUC并发编程【java提高】3
JUC并发编程【java提高】3
51 0
|
6月前
|
存储 缓存 Java
JavaSE基础篇:多线程
JavaSE基础篇:多线程
|
算法 NoSQL Java
并发编程系列教程(10) - 深入Java锁机制
并发编程系列教程(10) - 深入Java锁机制
41 0
|
缓存 Java 数据库连接
JUC并发编程【java提高】2
JUC并发编程【java提高】2
49 0
JUC并发编程学习(一)-什么是JUC
JUC并发编程学习(一)-什么是JUC
JUC并发编程学习(一)-什么是JUC
|
存储 SQL 设计模式
【JavaSE】之JUC并发编程(下)(二)
【JavaSE】之JUC并发编程(下)(二)
【JavaSE】之JUC并发编程(下)(二)
|
存储 前端开发 算法
【JavaSE】之JUC并发编程(下)(一)
【JavaSE】之JUC并发编程(下)(一)
【JavaSE】之JUC并发编程(下)(一)