JUC学习记录

前言

JUC学习记录,主要有lambda表达式之函数式编程线程的同步与通信如何关联Thread与Callable

一、Lambda表达式

package com.xhu.java.juc;

/**
 * 1. lambda表达式,(函数的形参原样copy) +  ->  +  {函数体}。
 * 2. @FunctionalInterface,该注解下的接口只有一个未实现函数。
 * 3. default
 * 4. static
 */
@FunctionalInterface
interface Add {
    /**
     * 两数相加
     *
     * @param x 加数
     * @param y 被加数
     * @return 两数之和
     */
    int add(int x, int y);

    /**
     * 函数式接口中可以有一个或多个默认实现方法
     */
    default void testDefault(){}

    /**
     * 函数式接口中可以有一个或多个静态方法
     */
    static void testStatic(){}
}

/**
 * 函数式编程展示
 */
public class LambdaTes {
    /**
     * 测试函数式接口中的方法
     */
    public static void test1() {
        Add addQuote = (x, y) -> {
            return x + y;
        };
        System.out.println(addQuote.add(2, 4));
    }

    public static void main(String[] args) {
        test1();
    }
}

二、多线程不一定块

package com.xhu.java.juc;

/**
 * 测试多线程快还是单线程快,考虑因素为:线程的创建和上下文切换花销的时间。
 */
public class ConcurrencyTest {
    //主、辅线程中需循环的次数
    public final static long COUNT = 1000000000;

    /**
     * 一个主线程和一个开辟的线程形成的多线程。
     *
     * @throws InterruptedException
     */
    private static void concurrency() throws InterruptedException {
        long start = System.currentTimeMillis();
        //开辟一个线程
        Thread thread = new Thread(() -> {
            int a = 0;
            for (int i = 0; i < COUNT; i++) {
                a += 5;
            }
        });
        //执行开辟的线程
        thread.start();
        //主线程执行内容
        int b = 0;
        for (int i = 0; i < COUNT; i++) {
            b--;
        }
        //开辟的 线程合并到主线程
        thread.join();
        //全程结束,计算花费的总时间
        long expense = System.currentTimeMillis() - start;
        //显示花费的总时间
        System.out.println("concurrency expense is at " + expense + "ms");
    }

    /**
     * 只有一个主线程执行,无并发
     */
    private static void serial() {
        long start = System.currentTimeMillis();
        int a = 0;
        for (int i = 0; i < COUNT; i++) {
            a += 5;
        }
        int b = 0;
        for (int i = 0; i < COUNT; i++) {
            b--;
        }
        long expense = System.currentTimeMillis() - start;
        System.out.println("serial expense is at " + expense + "ms");
    }

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

    }
}

三、线程

1、线程同步

A.传统的synchronized方法

package com.xhu.java.juc;


import java.util.concurrent.TimeUnit;

/**
 * 8锁机制,线程同步
 * Core:
 * 1.new 一份资源,synchronized对于成员方法锁的是this对象;对于类方法锁的是类对象即Class对象。
 * 2.new 两份资源,就有两个不同的this,但是类对象即Class对象只有一个。
 * 3.在synchronized的情况下,当前线程sleep时,并不会释放锁。
 * 4.非synchronized方法不参与锁竞争。(与其说去锁了this,还不如说synchronized方法要去争夺一个锁,这个锁是谁不重要,重要的是唯一的锁)
 * (而并不是用synchronized修饰的方法都是一个锁,修饰静态和非静态是两种锁)
 */
public class SynchronizedTest {
    public static void main(String[] args) {
        manipulateResource();
        System.out.println(new ClassLoader() {
        }.getName());
    }

    private static void manipulateResource() {
        //1.申请资源类
        Phone phone = new Phone();
        //2.多线程操作资源类
        new Thread(()->{phone.sendEmail();},"A").start();
        try {
            TimeUnit.MILLISECONDS.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{phone.sendSMS();},"B").start();
        new Thread(()->{phone.test();},"C").start();
    }
}

/**
 * 资源类,Phone,高内聚
 */
class Phone {
    int a = 2;
    /**
     * 发送邮件
     */
    public synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendEmail");
    }

    /**
     * 发送短信
     */
    public synchronized void sendSMS() {
        System.out.println("sendSMS");
    }
    public synchronized static void testStatic(){
        System.out.println("我锁this.getClass,与this无关");
    }
    public void test(){
        System.out.println(this.a);
        System.out.println("它们只是在争夺一个唯一的关于this的锁,并没有锁住this,我仍然可以操作this");
    }
}

B.ReentrantLock

package com.xhu.java.juc;

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

/**
 * 并发卖票,三个售票员卖出30张票
 * Core:在高内聚 低耦合的思想下,进行多线程 操作 共享资源(共享资源要高内聚,线程与资源之间是低耦合)
 *
 * @author lls
 */
public class SaleTicketsTest {
    public static void main(String[] args) {//程入的一个入口
        //1.创建资源类
        TicketResource tr = new TicketResource();
        //2.生成多个线程操作同一资源类
        new Thread(() -> {
            for (int i = 0; i < 30; i++) tr.sale();
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 30; i++) tr.sale();
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 30; i++) tr.sale();
        }, "C").start();
    }

}

/**
 * 高内聚,将共享资源抽象成类
 */
class TicketResource {
    //初始设定30张票
    private int ticketNum = 30;
    //申请可重入锁
    Lock lock = new ReentrantLock();

    /**
     * 卖票的操作,这也是高并发的源头
     */
    public void sale() {
        //在修改资源类时才加锁,减少锁的细粒度,提高并发量。
        try {
            lock.lock();
            if (ticketNum < 1) return;
            System.out.println(Thread.currentThread().getName() + "卖出了第" + ticketNum-- + "张票" + "还剩" + ticketNum + "张票");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

2、线程通信

A.统一唤醒(存在虚假唤醒)

package com.xhu.java.juc;

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

/**
 * 线程通信
 * 题目,两个线程,一个加1,一个减一,来10轮,该变量值不变。
 * Core:
 * 1.线程操作资源类
 * 2.判断/干活/通知,唤醒机制。
 * 3.防止虚假唤醒,需再次过判断,进行选择正常的操作,code体现在if 换 while进行循环+判断
 * summarization:高内聚低耦合+线程通信+while防止虚假唤醒。
 */
public class ThreadCommunicationTest {
    public static void main(String[] args) {
        test1();
    }

    private static void test1() {
        Resource resource = new Resource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

class Resource {
    //1.变量定义
    private int number = 0;
    //可重入非公平递归锁
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    //2.变量加1
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            //1.判断
            while (number > 1) {
                condition.await();
            }
            //2.干活
            number++;
            System.out.println(Thread.currentThread().getName() + " " + number);
            //3.通知
            condition.signalAll();
        } finally {
            lock.unlock();
        }

    }

    //3.变量减1
    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            //1.判断
            while (number < 1) {
                condition.await();
            }
            //2.干活
            number--;
            System.out.println(Thread.currentThread().getName() + " " + number);
            //3.通知
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    /*//2.变量加1
    public synchronized void increment() throws InterruptedException {
        //1.判断
        while (number > 1) {
            this.wait();
        }
        //2.干活
        number++;
        System.out.println(Thread.currentThread().getName() + " " + number);
        //3.通知
        this.notifyAll();
    }

    //3.变量减1
    public synchronized void decrement() throws InterruptedException {
        //1.判断
        while (number < 1) {
            this.wait();
        }
        //2.干活
        number--;
        System.out.println(Thread.currentThread().getName() + " " + number);
        //3.通知
        this.notifyAll();
    }*/

}

B.新版Lock之定向唤醒

package com.xhu.java.juc;

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

/**
 * 精准通知
 * A线程 -》 B线程 -》C线程,10轮,按顺序定点通知。
 * Core:用Condition类,类似于操作系统中线程阻塞分类为几个阻塞队列,再定向唤醒。
 */
public class DelicateSignal {
    public static void main(String[] args) {
        test1();
    }

    private static void test1() {
        ShareData sd = new ShareData();
        new Thread(() -> {
            for (int i = 0; i < 3; i++) sd.aMethod();
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 3; i++) sd.bMethod();
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 3; i++) sd.cMethod();
        }, "C").start();


    }
}

/**
 * 共享资源
 */
class ShareData {
    //定点通知,0表示A,1表示B,2表示C
    private int mark = 0;
    //生成锁,lock是一把锁,一个人拿去用了,另一个想用这一个唯一的锁就必须等待,(大家争夺该锁时需同步)
    private Lock lock = new ReentrantLock();
    //生成多个condition,将阻塞线程分类,挂到多个不同的列表上
    Condition c1 = lock.newCondition();
    Condition c2 = lock.newCondition();
    Condition c3 = lock.newCondition();

    //A工作内容
    public void aMethod() {
        System.out.println("A想用锁之前");
        lock.lock();
        System.out.println("A想用锁之后");
        try {
            while (mark != 0) {
                System.out.println("A开始等待");
                c1.await();
            }
            mark = ++mark % 3;
            System.out.println("A完成自己的工作内容,B来处理后续工作");
            c2.signal();
            System.out.println("通知阻塞的B线程");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //B工作内容
    public void bMethod() {
        System.out.println("B想用锁之前");
        lock.lock();
        System.out.println("B想用锁之后");
        try {
            while (mark != 1) {
                System.out.println("B开始等待");
                c2.await();
            }
            mark = ++mark % 3;
            System.out.println("B完成自己的工作内容,C来处理后续工作");
            c3.signal();
            System.out.println("通知阻塞的C线程");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //C工作内容
    public void cMethod() {
        System.out.println("C想用锁之前");
        lock.lock();
        System.out.println("C想用锁之后");
        try {

            while (mark != 2) {
                System.out.println("C开始等待");
                c3.await();
            }
            mark = ++mark % 3;
            System.out.println("C完成自己的工作内容,A来处理新的一轮工作");
            c1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            System.out.println("通知阻塞的A线程");
        }
    }
}
/*
A想用锁之前
A想用锁之后
A完成自己的工作内容,B来处理后续工作
通知阻塞的B线程
A想用锁之前
A想用锁之后
A开始等待
B想用锁之前
B想用锁之后
B完成自己的工作内容,C来处理后续工作
通知阻塞的C线程
B想用锁之前
B想用锁之后
B开始等待
C想用锁之前
C想用锁之后
C完成自己的工作内容,A来处理新的一轮工作
通知阻塞的A线程
A完成自己的工作内容,B来处理后续工作
通知阻塞的B线程
A想用锁之前
A想用锁之后
A开始等待
B完成自己的工作内容,C来处理后续工作
通知阻塞的C线程
B想用锁之前
B想用锁之后
B开始等待
C想用锁之前
C想用锁之后
C完成自己的工作内容,A来处理新的一轮工作
通知阻塞的A线程
C想用锁之前
C想用锁之后
C开始等待
A完成自己的工作内容,B来处理后续工作
通知阻塞的B线程
B完成自己的工作内容,C来处理后续工作
通知阻塞的C线程
C完成自己的工作内容,A来处理新的一轮工作
通知阻塞的A线程

Process finished with exit code 0
 */

四、安全的Collection

package com.xhu.java.juc;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * 1.为什么线程不安全?
 * 同时操作一个资源类,导致读写异常,或丢失修改。
 * 2.如何解决?
 * 用现由的并发包,原理为,读写分离与加锁
 */
public class ArrayListTest {
    //线程不安全,没有加任何同步机制
    public static List<String> list = new ArrayList<>();
    //线程安全,写了一个继承SynchronizedCollection<E>的静态内部类,里面有ArrayList一样的功能,该子类不仅实现了List,还加了同步代码块,锁了this
    public static List<Integer> safeList = Collections.synchronizedList(new ArrayList<>());
    //读写分离,读的时候不加锁,提高并发,写的时候加锁,写的时候是写在了新数组里面,然后改变数组引用。(分两份,读用原本的,写时复制一份)
    public static List<Integer> safeList2 = new CopyOnWriteArrayList<>();
    //类似于上面的CopyOnWriteArrayList
    public static Set<Integer> safeSet = new CopyOnWriteArraySet<>();
    //为synchronized去锁new Node节点。
    public static Map<String,String> safeMap = new ConcurrentHashMap<>();
    public static void testArrayList() {
        concurrentForList();
        try {
            Thread.sleep(2000);
            System.out.println(list.size());
            Thread.sleep(1000);
            System.out.println(list.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    private static void concurrentForList() {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                for (int j = 0; j < 100000; j++) {
                    synchronized (list) {
                        list.add(UUID.randomUUID().toString().substring(0, 9));
                    System.out.println(list);
                    }
                }
            }, String.valueOf(i)).start();
        }
    }

    public static void main(String[] args) {
        testArrayList();
    }
}

五、FutureTask关联Callable

package com.xhu.java.juc;

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

/**
 * 有返回值的线程操作类Callable
 * Core:FutureTask类搭线Runnable 和 Callable,而Runnable与Thread搭线,所以可将Callable与Thread关联起来。
 * 注:这是线程的第三种创建方式,Thread Runnable Callable 线程池
 */
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        test1();
    }

    private static void test1() throws InterruptedException, ExecutionException {

        FutureTask<Integer> f = new FutureTask<>(new MyCallable());
        new Thread(f, "A").start();
        Integer i = f.get();
        System.out.println(i);
    }
}

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        return 1024;
    }
}

总结

1)Java8后支持的函数式编程,专注核心逻辑,实现一定义,多种快速重写。
2)线程的同步与通信,线程的调度必须的两点。
3)老版synchronized和新版Lock,新版功能强大。
4)新版安全的Collection,主要有读写分离思想的写时复制。
5)Callable关联Thread,靠的是中间子类FutureTask。

参考文献

[1] JUC 尚硅谷周阳

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值