笔记:JUC

JUC

JUC:java.util.concurrent
有两个子包
java.util.concurrent.atomic
java.util.concurrent.locks

需要掌握什么是:进程/线程 并发/并行

回顾:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
* 使用高内聚低耦合的方法创建卖票实例(三个售票员,三十张票)
* :线程/操作/资源类(资源类包含操作(票有sale方法))
* 线程并不是.start之后就开始调用
*
* */
class Ticket{
    // 初始票数
    private  int num = 30;
    Lock lock = new ReentrantLock();
    public void sale(){
        lock.lock();
        try{
            if (num>0){
                System.out.println(Thread.currentThread().getName()+"卖"+num-- +"票");
                Thread.sleep(10);
            }else {
                System.out.println("没票了");
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}
public class SaleTicketDemo01 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
//        Thread t1 = new Thread();企业级怎么写?
//        Thread t2 = new Thread();
//        Thread t3 = new Thread();
        //Thread(Runnable target, String name)分配一个新的 Thread对象。
        //匿名内部类
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                for (int i = 0; i < 10; i++) {
//                    ticket.sale();
//                }
//            }
//        },"售票员1").start();
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                for (int i = 0; i < 10; i++) {
//                    ticket.sale();
//                }
//            }
//        },"售票员3").start();
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                for (int i = 0; i < 10; i++) {
//                    ticket.sale();
//                }
//            }
//        },"售票员2").start();
        new Thread(() -> {for (int i = 0; i < 10; i++)ticket.sale();},"售票员1").start();
        new Thread(()->{for (int i = 0; i < 10; i++)ticket.sale();},"售票员2").start();
        new Thread(()->{for (int i = 0; i < 10; i++)ticket.sale();},"售票员3").start();
    }

Lambda写法:

前提要求

必须要是函数式接口(@FunctionalInterface表示函数式接口,只能声明一个方法,但是可以声明多个静态方法或default方法)

写法:复制小括号,写箭头,写大括号
例如:

package org.yyx.lambda;

/*
* @auther yyx
* Lambda表达式写法:(函数式接口、复制小括号,写箭头,写大括号)
* @FunctionalInterface表示函数式接口,只能声明一个方法,但是可以声明多个静态方法或default方法
* */
@FunctionalInterface
interface Yyx{
    //public void write();
    public int add(int x,int y);
    default void mul(int x,int y){
        System.out.println(x*y);
    }
    static int div(int x,int y){
        return x/y;
    }
}
public class LambdaExpressDemo {
    public static void main(String[] args) {
        /*
        Yyx yyx = new Yyx() {
            @Override
            public void write() {
                System.out.println("Yyx匿名内部类输出");
            }
        };
        yyx.write();
         */
        // Lambda表达式
        /*Yyx yyx = () -> {
            System.out.println("Lambda表达式输出");
        };
        yyx.write();
         */
        // 含参数的Lambda表达式
        Yyx yyx = (int x,int y) -> {
            System.out.println("Lambda表达式中add");
            return x+y;
        };
        System.out.println(yyx.add(7, 5));

         yyx.mul(7,6);
        System.out.println(Yyx.div(6, 2));
    }
}

集合类不安全

以ArrayList为例

/*
* 故障现象(报错):java.util.ConcurrentModificationException表示高并发错误
* 导致原因:多线程并发争抢数据没有加锁
* 解决方法:
* 1. new Vector<>();线程安全
* 2. Collections.synchronizedList(new ArrayList<>());工具类上锁
* 3. CopyOnWriteArrayList<>();
* 优化建议:
* */
public class NotSateDemo {
    // 以ArrayList为例
    public static void main(String[] args) {
        List<String> list = new CopyOnWriteArrayList<>(); //Collections.synchronizedList(new ArrayList<>()); //new Vector<>();//new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            new Thread(()->{list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

写时复制

CopyOnWriteArrayList

即写时复制的容器,往一个容器添加元素时,不直接往当前Object[]添加,而是先将容器进行copy,复制出一个新的Object【】newElements,然后新的Object[ ] newElements添加元素,之后再将原容器的引用指向新的容器 setArray(newElements),这样做的好处时可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素,所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

public boolean add(E e) {
        synchronized(this.lock) {
            Object[] es = this.getArray();
            int len = es.length;
            es = Arrays.copyOf(es, len + 1);
            es[len] = e;
            this.setArray(es);
            return true;
        }
    }

注意:ArrayList、HashSet、HashMap都是线程不安全的,解决方法都可以用
CopyOnWriteArrayList、CopyOnWriteHashSet、CopyOnWriteHashMap

锁机制

思考:

/*
* 1.标准方法,先输出A还是B
* 2.暂停四秒在A方法中,先输出A还是B
* 3.新增一个输出C的方法,请问先输出A还是C
* 4.两个打印机,先输出A还是B
* 5.两个静态同步方法,同一台打印机,先输出A还是B
* 6.两个静态同步方法,两台打印机,先输出A还是B
* 7.一个静态同步方法,一个普通同步方法,同一台打印机,先输出A还是B
* 8.一个静态同步方法,一个普通同步方法,两台打印机,先输出A还是B
*
*
* */
  • 1.标准方法,先输出A还是B 随机
  • 调用了资源类中的任一同步方法,synchronized锁的并不是一个方法,而是整个资源类对象,这时不能调用其它的同步方法
  • 2.暂停四秒在A方法中,先输出A还是B A
  • 3.新增一个输出C的普通方法,请问先输出A还是C(A还有四秒暂停) C
  • 加普通方法与资源类无关。
  • 4.两个打印机,先输出A还是B
  • 先输出B(因为A sleep,如果不sleep,随机)
  • 5.两个静态同步方法,同一台打印机,先输出A还是B(静态同步锁)
  • static锁的是全局锁(锁了整个类Sout Class对象) A
  • 6.两个静态同步方法,两台打印机,先输出A还是B
  • static锁的是全局锁(锁了整个类Sout Class对象) A
  • 7.一个静态同步方法,一个普通同步方法,同一台打印机,先输出A还是B AB(锁的是全局Sout和锁的是sout,锁的是两个东西)
  • 8.一个静态同步方法,一个普通同步方法,两台打印机,先输出A还是B AB
线程通信

记得防止虚假唤醒,两种方式都是对的,一种是旧版本,一种是JUC。

package org.yyx.comunication;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
* 资源类(附带该类的操作方法)
* 线程之间通信
* 一个线程加一,一个线程减一,来回十次
* 多线程(只有两个线程情况下,只用if就可以解决,但是多线程情况,容易造成虚假唤醒,所以,wait用while循环)
*
* 1.高内聚低耦合情况下(线程类操作资源类)
* 2.判断、操作、通知
* 3.防止虚假唤醒(多个线程,wait操作还是if)
*
* */
class Number{
    private int num = 0;
    // 用JUC来写
    private Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public void add(){
        lock.lock();
        try {
            // 判断
            while (num != 0) {
                condition.await();
            }
            // 干活
            num++;
            System.out.println(Thread.currentThread().getName()+"   "+num);
            // 通知
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void de(){
        lock.lock();
        try {
            // 判断
            while (num == 0) {
                condition.await();
            }
            // 干活
            num--;
            System.out.println(Thread.currentThread().getName()+"   "+num);
            // 通知
            condition.signalAll();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    /*public synchronized void add(){
        // 判断
        while (num != 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 操作
        num++;
        System.out.println(Thread.currentThread().getName()+"   "+num);
        // 通知
        this.notifyAll();
    }
    public synchronized void de(){
        // 判断
        while (num == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 操作
        num--;
        System.out.println(Thread.currentThread().getName()+"   "+num);
        // 通知
        this.notifyAll();
    }
     */
}
public class ProdConsumerDemo {
    public static void main(String[] args) {
        // 创建资源对象
        Number number = new Number();
        new Thread(()->{ for (int i = 0; i < 10; i++) number.add();},"A").start();
        new Thread(()->{ for (int i = 0; i < 10; i++) number.de();},"B").start();
        new Thread(()->{ for (int i = 0; i < 10; i++) number.add();},"C").start();
        new Thread(()->{ for (int i = 0; i < 10; i++) number.de();},"D").start();
    }
}

线程之间的精确唤醒

有几点要注意以下:

1.用JUC写必须记得lock.lock
2.千万不能忘记唤醒另一个线程之前要修改标记

package org.yyx.comunication;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
* 线程之间的顺序调用(精确调用)实现A->B->C
* 三个线程启动,要求:
* AA打印5次,BB打印10次,CC打印15次,接着在打印
* AA打印5次,BB打印10次,CC打印15次
* */
class Word {// 资源类
    // 设置一个锁
    private Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();
    // 设置一个标志
    private int num = 1;
    // 设置打印次数函数
    public void printTimes(int i){
        for (int j = 0; j < i; j++) {
            System.out.println(Thread.currentThread().getName()+"  "+i);
        }
    }
    public void A(){
        try {
            lock.lock();
            // 判断
            while (num != 1){
                condition1.await();
            }
            // 操作
            printTimes(5);
            // 唤醒
            // 唤醒之前必须修改标记
            num = 2;
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void B(){
        try {
            lock.lock();
            // 判断
            while (num != 2){
                condition2.await();
            }
            // 操作
            printTimes(10);
            // 唤醒
            num = 3;
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void C(){
        try {
            lock.lock();
            // 判断
            while (num != 3){
                condition3.await();
            }
            // 操作
            printTimes(15);
            // 唤醒
            num = 1;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
public class ConditionDemo {
    public static void main(String[] args) {
        // 实例化资源类
        Word word = new Word();
        new Thread(()->{for (int i = 0; i < 10; i++) word.A();},"AA").start();
        new Thread(()->{for (int i = 0; i < 10; i++) word.B();},"BB").start();
        new Thread(()->{for (int i = 0; i < 10; i++) word.C();},"CC").start();
    }
}

第三种线程方法

重点:

和Runnable相比区别:重载方法不同,有返回值,有泛型

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/*
* 第三种线程方法(之前两种是线程类和Runnable接口)
* Callable接口
* */
class MyThread implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("进入call方法");
        return 520;
    }
}
public class NewThread {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread myThread = new MyThread();
        FutureTask<Integer> futureTask = new FutureTask(myThread);
        new Thread(futureTask,"A").start(); // 进入call方法
        System.out.println(futureTask.get()); // 520
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值