JUC并发类

JUC并发

1、线程和进程

线程和进程

进程:一个程序 程序的集合

一个进程往往可以包含多个线程,至少包含一个。

Java默认2个线程:main、GC

**java真的可以开启线程吗?**不能

//通过本地方法,调用底层c++,java无法直接操作硬件

private native void start0();

并发、并行

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

  • CPU一核,快速交替

并行

  • CPU多核,多个线程同时执行;线程池

线程有几个状态

public enum State{
    //新生
    NEW,
    
    //运行
    RUNNABLE,
    
    //阻塞
    BLOCKED,
        
    //等待,死死地等
    WAITING,
    
    //超时等待
    TIMED_WAITING,
    
    //终止
    TERMINATED;
}

wait/sleep区别

1、来自不同的类

wait–Object

sleep–Thread

2、关于锁的释放

wait会释放锁,sleep会抱住锁

3、使用的范围不同

wait必须在同步代码块中

sleep可以在任何地方

4、是否捕获异常

wait不用,中断异常

sleep用

2、Synchronized和Lock锁

ReentrantLock

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gjYwXV8a-1603975581250)(E:\桌面\images\1603803090(1)].jpg)

公平锁:十分公平,先来后到

非公平锁:不公平,可以由cpu调度

Synchronized 和 Lock 的区别

  • Synchronized 内置的java关键字,Lock是一个java类
  • Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
  • Synchronized 会自动释放锁,Lock要手动unLock();
  • Synchronized 线程1(获得锁,阻塞)线程2(等待),Lock锁就不一定会等下去;
  • Synchronized 可重入锁,不可以中断,非公平,Lock,可重入锁,可以判断锁,非公平(可以自己设置)
  • Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的代码同步

Synchronized问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OqN3Twew-1603975581254)(E:\桌面\images\1603808392(1)].png)

if改为while判断:

就是用if判断的话,唤醒后线程会从 wait 之后的代码开始运行,但是不会重新判断if条件,直接继续运行 if 代码块之后的代码,而如果使用 while 的话,也会从 wait 之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行 while 代码块之后的代码块,成立的话继续wait。

Lock锁问题 JUC版

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yG0UNepW-1603975581257)(E:\桌面\images\1603809475(1)].png)

Lock lock = new ReentrantLock();
Condition condition  = lock.newCondition(); 
condition.await();
condition.signalAll();

Condition 精准的通知和唤醒线程

Lock lock = new ReentrantLock();
Condition condition1  = lock.newCondition();
Condition condition2  = lock.newCondition();
Condition condition3 = lock.newCondition();
testA(){
    while(num!=1){
    condition1.await();
     }
    num = 2;
    condition2.siganl();
}
testB(){
    while(num!=2){
    condition2.await();
     }
    num = 2;
    condition3.siganl();
}
testC(){
   while(num!=3){
    condition3.await();
     }
    num = 1;
    condition1.siganl();
}

3、8锁现象

//1、标准情况下,先发短信
//2、sendSms延迟4秒,先发短信
public class Test1 {

    public static void main(String[] args) {
        Phone phone=new Phone();

        //锁的存在
        new Thread(()->{phone.sendSms();},"A").start();
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(()->{phone.call();},"B").start();

    }
}


class  Phone{
    //synchronized 锁的对象是方法的调用者!
    //两个方法是同一把锁,谁先拿到谁执行
    public synchronized void sendSms(){
        try{
            TimeUnit.SECONDS.sleep(4);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }

}
//3,增加了一个普通方法,先执行发短信还是hello?  普通方法hello
//4,两个对象,两个同步方法,发短信还是打电话?打电话
public class Test2 {

    public static void main(String[] args) {
        //两个对象,两个调用者,两把锁!看时间
        Phone2 phone1=new Phone2();
        Phone2 phone2=new Phone2();

        //锁的存在
        new Thread(()->{phone1.sendSms();},"A").start();
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(()->{phone2.call();},"B").start();

    }
}


class  Phone2{
    //synchronized 锁的对象是方法的调用者!
    //两个方法是同一把锁,谁先拿到谁执行
    public synchronized void sendSms(){
        try{
            TimeUnit.SECONDS.sleep(4);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
    //这里没有锁,不是同步方法,不受锁的影响
    public  void hello(){
        System.out.println("hello");
    }

}
//5,增加两个静态的同步方法,只有一个对象,先打印发短信还是打电话?发短信
//6,两个对象,增加两个静态的同步方法,只有一个对象,先打印发短信还是打电话?发短信
public class Test3 {

    public static void main(String[] args) {
        //两个对象的Class模板只有一个,static锁的模板
        Phone3 phone1=new Phone3();
        Phone3 phone2=new Phone3();

        //锁的存在
        new Thread(()->{phone1.sendSms();},"A").start();
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(()->{phone2.call();},"B").start();

    }
}

//Phone3 唯一的一个Class对象
class  Phone3{
    //synchronized 锁的对象是方法的调用者!
    //static 静态方法,类一加载就有了,锁的是Class模板
    public static synchronized void sendSms(){
        try{
            TimeUnit.SECONDS.sleep(4);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public static synchronized void call(){
        System.out.println("打电话");
    }
}
//7、1个静态同步方法,1个普通的同步方法,一个对象,先打印发短信还是打电话?打电话
//8、1个静态同步方法,1个普通的同步方法,两个对象,先打印发短信还是打电话?打电话
public class Test4 {

    public static void main(String[] args) {

        Phone4 phone1=new Phone4();
        Phone4 phone2=new Phone4();

        //锁的存在
        new Thread(()->{phone1.sendSms();},"A").start();
        try{
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(()->{phone2.call();},"B").start();

    }
}

//Phone3 唯一的一个Class对象
class  Phone4{

    public static synchronized void sendSms(){
        try{
            TimeUnit.SECONDS.sleep(4);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public  synchronized void call(){
        System.out.println("打电话");
    }
}

小结

new this 具体的一个对象

static Class 唯一的模板

4、集合类不安全

ConcurrentModificationException :并发修改异常

并发下ArrayList不安全

解决方案:

List<String> list = new Vector();
List<String> list = Collections.synchronizedList(new ArrayList<>());
List<String> list = new CopyOnWriteArratList<>();

CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略

多个线程调用的时候,list,读取的时候,固定的 ,写入(覆盖)

在写入的时候避免覆盖,造成数据问题

CopyOnWriteArratList和Vector区别

CopyOnWriteArratLis – Lock锁

Vector – synchronized

set不安全

Set<String> set = Collections.synchronizedSet(new HashSet<>());
List<String> list = new CopyOnWriteArratSet<>();

hashSet底层–hashMap

add set本质就是map key是无法重复的

public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

map不安全

Set<String> set = Collections.synchronizedMap(new HashMap<>());
List<String> list = new ConcurrentHashMap<>();

5、Callable

可以有返回值,可以抛出异常,方法不同,run()/call()

代码

Callable<T>
T call(){}
//泛型与返回值要相同
    
new Thread(new FutureTask<V>(Callable)).start();   //适配器模式 
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable=new MyCallable();
        FutureTask futureTask =new FutureTask(myCallable);//适配类
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();//结果会被缓存
        
        Integer o = (Integer) futureTask.get();//返回结果 ,可能会产生阻塞,把他放到最后
        //或者异步通信来处理
        System.out.println(o);
    }
}

class MyCallable implements Callable<Integer> {

    @Override
    public Integer call()  {
        System.out.println("call()");
        return 1024;
    }
}

细节:

  • 有缓存
  • 结果可能需要等待,会阻塞

6、 常用辅助类

6.1、CountDownLatch

减法计数器

public class CountDownLatchTest {
    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()+"GO OUT");
                countDownLatch.countDown();//数量-1
            },String.valueOf(i)).start();
        }
        countDownLatch.await();//等计数器归零,然后再向下执行
        System.out.println("close door");
    }
}

每次有线程调用countDown()数量-1,假设计数器为0,countDownLatch.await()就会被唤醒,继续执行。

6.2、CyclicBarrier

加法计数器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Xnz6KBQ-1603975581260)(E:\桌面\images\1603884516(1)].png)

public class CyclicBarrierTest {
    public static void main(String[] args) {
        //传Runnable
        CyclicBarrier  cyclicBarrier=new CyclicBarrier(7,()->{
            System.out.println("召唤成功");
        });

        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();
                }
            }).start();
        }
    }
}

6.3、Semaphore

Semaphore:信号量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bmx0tq3G-1603975581262)(E:\桌面\images\1603890296(1)].png)

public class SemaphoreDemo {
    public static void main(String[] args) {
        //线程数量   操作系统中叫可用资源的数目
        Semaphore semaphore = new Semaphore(3);

        for (int i = 1; i <=6; i++) {
            new Thread(()->{
                //acquire() 得到
                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 {
                    // release() 释放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

//acquire() 获到,假设如果已经满了,等待,等待被释放为止

// release() 释放,会将当前的信号量释放+1,然后唤醒等待的线程

作用:多个共享资源互斥的使用,并发限流,控制最大的线程数

7、读写锁

ReadWriteLock

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6PlQ93u3-1603975581263)(E:\桌面\images\1603898258(1)].png)

/**
 * 独占锁(写锁) 一次只能被一个线程占有
 * 共享锁(读锁)多个线程可以同时占有
 * ReadWriteLock
 * 读-读  可以共存
 * 读-写  不能共存
 * 写-写  不能共存
 */

public class ReadWriteLockTest {
    public static void main(String[] args) {
        MyCache myCache=new MyCache();

        for (int i = 1; i <= 7; i++) {
            final int temp = i;
            new Thread(()->{
                myCache.put(temp+"",temp+"");
            },String.valueOf(i)).start();
        }

        for (int i = 1; i <= 7; i++) {
            final int temp = i;
            new Thread(()->{
                myCache.get(temp+"");
            },String.valueOf(i)).start();
        }
        
    }
}


class MyCache{
    private volatile Map<String,Object> map=new HashMap<>();
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    public void put(String key,Object value){
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入Ok");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.writeLock().unlock();
        }
    }

    public void get(String key){
        lock.readLock().lock();
        try{
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取Ok");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.readLock().unlock();
        }
    }
}

8、阻塞队列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VzdbHzz5-1603975581264)(E:\桌面\images\1603900482(1)].png)

阻塞队列;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mxC9haao-1603975581265)(E:\桌面\images\1603900628(1)].png)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-drkRXKmR-1603975581267)(E:\桌面\images\1603902387(1)].png)

BlockingQueue

多线程并发处理,线程池

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D8wuf377-1603975581268)(E:\桌面\images\1603902614(1)].png)

使用队列

添加、移除

四组API

方式抛出异常有返回值,不抛出异常阻塞等待超时等待
添加add()offer(),falseput()offer(E,time,TimeUnit)
移除remove()poll(),nulltake()poll(time,TimeUnit)
判断队首element()peek()
public class BQTest {
    public static void main(String[] args) {
    test1();
    test2();
    }

    public static void test1(){
        ArrayBlockingQueue blockingQueue =new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        //查看队首元素
        System.out.println(blockingQueue.element());
        //java.lang.IllegalStateException: Queue full  抛出异常
        // System.out.println(blockingQueue.add("d"));
        System.out.println("=============");
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //NoSuchElementException 抛出异常
        //System.out.println(blockingQueue.element());
        //java.util.NoSuchElementException  抛出异常
        //System.out.println(blockingQueue.remove());
    }

    public static void test2(){
        ArrayBlockingQueue blockingQueue =new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.peek());
        // false 不抛出异常
        //System.out.println(blockingQueue.offer("d"));

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        // null 不抛出异常
        System.out.println(blockingQueue.poll());
    }
    
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue blockingQueue =new ArrayBlockingQueue<>(3);

        //一直阻塞
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
//        blockingQueue.put("d");   //队列没有位置了,一直阻塞

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
//        System.out.println(blockingQueue.take());   //没有这个元素,一直阻塞
    }
}

9、同步队列

SynchronousQueue

没有容量,

进去一个元素,必须等待取出来之后,才能再往里面放一个元素(相当于容量1)

public class SynchronousQueueTest {
    public static void main(String[] args) {
        SynchronousQueue<String> synchronousQueue = new SynchronousQueue();//同步队列

        new Thread(()->{
            try {
            System.out.println(Thread.currentThread().getName()+"put 1");
            synchronousQueue.put("1");
            System.out.println(Thread.currentThread().getName()+"put 2");
            synchronousQueue.put("2");
            System.out.println(Thread.currentThread().getName()+"put 3");
            synchronousQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T1").start();

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"=>"+synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"=>"+synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"=>"+synchronousQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"T2").start();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值