并发草稿完结版1

并发

线程的基本机制

Join()

image-20210902105546142

测试代码
package com.lxy.二刷Java编程思想.基本线程机制.加入一个线程;

import java.util.concurrent.TimeUnit;

/**
 * @author luxiaoyang
 * @create 2021-09-02-10:42
 */
public class JoinTest {
    public static void main(String[] args) {
        A a = new A("A",1);
        B b = new B("B", a);
        a.interrupt();
    }
}

class A extends Thread {
    private int duration;

    public A(String name, int duration) {
        super(name);
        this.duration = duration;
        start();
    }

    @Override
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(duration);
        } catch (InterruptedException e) {
            System.out.println(getName() + " is interrupted" + "isInterrupted() :" + isInterrupted());
        }
        System.out.println("A finished");
    }
}

class B extends Thread {
    private A a;

    public B(String name,A a) {
        super(name);
        this.a = a;
        start();
    }

    @Override
    public void run() {
        try {
            a.join();
        } catch (InterruptedException e) {
            System.out.println(getName() + "is interrupted");
        }
        System.out.println(getName() + "finished");
    }
}

捕获异常

由于线程的本质特性,我们无法直接捕获由从线程中抛出的异常,一旦在run()方法中抛出异常,那么该异常就会传播到控制台,由JVM抛出。下面是代码示例:

package com.lxy.二刷Java编程思想.基本线程机制.捕获异常;

/**
 * @author luxiaoyang
 * @create 2021-09-02-11:04
 */
public class CatchExceptionTest {
    public static void main(String[] args) {
        try {
            new A();
        } catch (Exception e) {
            System.out.println("Catched");
        }
    }
}

class A implements Runnable {
    private Thread t = new Thread(this);

    public A() {
        t.start();
    }

    @Override
    public void run() {
        try {
            System.out.println("make sure code run");
            int i = 1 / 0;
        } catch (Exception e) {
            throw new ArithmeticException(e.toString());
        }
    }
}

// 输出:

/*  make sure code run
	Exception in thread "Thread-0" java.lang.ArithmeticException: java.lang.ArithmeticException: / by zero
	at com.lxy.二刷Java编程思想.基本线程机制.捕获异常.A.run(CatchExceptionTest.java:30)
	at java.lang.Thread.run(Thread.java:748)*/

//  可见程序的运行正如上面所说,CatchExceptionTest.main()中的try-catch块并没有捕获到由A.run()所抛出的异常,而是直接由控制台打印
*****

​ 一开始我理解的是,可以由Thread.UncaughtExceptionHnadler实现将线程的异常传递给“调用”它的线程,但是发现这样也不行。那么可以强调一点,线程的异常必须在run()中得到妥善处理,或者通过下面的方式,总之,是无法传递回来的:

package com.lxy.二刷Java编程思想.基本线程机制.捕获异常;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * @author luxiaoyang
 * @create 2021-09-02-11:19
 */
public class UncaughtExceptionHandler {
    public static void main(String[] args) {
        try {
            ExecutorService exec = Executors.newCachedThreadPool(new MyThreadFactory());

            exec.execute(new C());

            exec.shutdown();
        } catch (Exception e) {
            System.out.println("main() caughted the C.run() exception");
        }
    }
}

class C implements Runnable {
    @Override
    public void run() {
        Thread t = Thread.currentThread();
        System.out.println("Run by : " + t);
        System.out.println("eh: " + t.getUncaughtExceptionHandler());
        int i = 1 / 0;
    }
}

class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        throw new RuntimeException(e);
    }
}

class MyThreadFactory implements ThreadFactory {
    @Override
    public Thread newThread(Runnable r) {
        System.out.println(this + "creating new thread");
        Thread t = new Thread(r);
        System.out.println("created: " + t);
        t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        System.out.println("eh = " + t.getUncaughtExceptionHandler());
        return t;
    }
}

/*
输出: 
    com.lxy.二刷Java编程思想.基本线程机制.捕获异常.MyThreadFactory@4459eb14creating new thread
    created: Thread[Thread-0,5,main]
    eh = com.lxy.二刷Java编程思想.基本线程机制.捕获异常.MyUncaughtExceptionHandler@5a2e4553
    Run by : Thread[Thread-0,5,main]
    eh: com.lxy.二刷Java编程思想.基本线程机制.捕获异常.MyUncaughtExceptionHandler@5a2e4553

    Exception: java.lang.RuntimeException thrown from the UncaughtExceptionHandler in thread "Thread-0"
*/

// 可见,并未输出UncaughtExceptionHandler.main() catch块中的内容,由此可见以上论述!

从任务中产生返回值

image-2021090215119243

package com.lxy.二刷Java编程思想.基本线程机制.从任务中产生返回值;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author luxiaoyang
 * @create 2021-09-02-11:44
 */
public class TaskWithResult implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "Test task with result";
    }

    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        List<Future<String>> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(exec.submit(new TaskWithResult()));
        }
        list.forEach(f -> {
            // f.get()将阻塞,直至结果准备就绪
            try {
                System.out.println(f.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
                return;
            } catch (ExecutionException e) {
                e.printStackTrace();
            } finally {
                exec.shutdown();
            }
        });
    }
}

FutureTask

image-20210902143654309

Java并发编程Future超详细教程

image-20210902155649925

线程池:
@Configuration
public class ThreadPoolConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(){
/**
* 核心线程数
* 拥有最多线程数
* 表示空闲线程的存活时间
* 存活时间单位
* 用于缓存任务的阻塞队列
* 省略:
*  threadFactory:指定创建线程的工厂
*  handler:表示当workQueue已满,且池中的线程数达到maximumPoolSize时,线程池拒绝添加新任务时采取的策略。
*/
return new ThreadPoolExecutor(50,500,30, TimeUnit.SECONDS,new ArrayBlockingQueue<> (10000));
    }
}
异步编排:
 @Override
    public Map<String, Object> getBySkuId(Long skuId) {

        Map<String, Object> result = new HashMap<>();

        //1. skuInfoCompletableFuture
        CompletableFuture<SkuInfo> skuInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            // 通过skuId查询skuInfo
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            // 保存skuInfo
            result.put("skuInfo", skuInfo);
            return skuInfo;
        }, threadPoolExecutor);

        //2. spuSaleAttrListFuture
        CompletableFuture<Void> spuSaleAttrListFuture = skuInfoCompletableFuture.thenAcceptAsync(skuInfo -> {
            // 销售属性-销售属性值回显并锁定
            List<SpuSaleAttr> spuSaleAttrList = productFeignClient.getSpuSaleAttrListCheckBySku(skuInfo.getId(), skuInfo.getSpuId());
            // 保存数据
            result.put("spuSaleAttrList", spuSaleAttrList);
        }, threadPoolExecutor);

        // 3. skuValueIdsMapFuture
        CompletableFuture<Void> skuValueIdsMapFuture = skuInfoCompletableFuture.thenAcceptAsync(skuInfo -> {
            //根据spuId查询map 集合属性
            Map skuValueIdsMap = productFeignClient.getSkuValueIdsMap(skuInfo.getSpuId());
            //保存 json字符串
            String valuesSkuJson = JSON.toJSONString(skuValueIdsMap);
            // 保存valuesSkuJson
            result.put("valuesSkuJson", valuesSkuJson);
        }, threadPoolExecutor);

        //4. priceFuture
        CompletableFuture<Void> priceFuture = CompletableFuture.runAsync(() -> {
            //获取商品最新价格
            BigDecimal skuPrice = productFeignClient.getSkuPrice(skuId);
            // 获取价格
            result.put("price", skuPrice);
        }, threadPoolExecutor);

        //5. categoryViewFuture
        CompletableFuture<Void> categoryViewFuture = skuInfoCompletableFuture.thenAcceptAsync(skuInfo -> {
            //获取商品分类
            BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
            //保存商品分类数据
            result.put("categoryView", categoryView);
        }, threadPoolExecutor);

        CompletableFuture<Void> incrHotScoreCompletableFuture =
                CompletableFuture.runAsync(() -> {
                    serviceListFeign.incrHotScore(skuId);
                }, threadPoolExecutor);

        CompletableFuture.allOf(skuInfoCompletableFuture,
                spuSaleAttrListFuture,
                skuValueIdsMapFuture,
                priceFuture,
                categoryViewFuture).join();
        return result;
    }

共享受限资源

synchronized锁机制

image-20210902164901367

显示的互斥机制Lock锁

与synchronized的比较:

image-20210902171503112

lock.tryLock();
lock.reyLock(2,TimeUnit.SECONDS);

原子性与易变性

​ 原子操作是不能被线程调度机制中断的操作;一旦操作开始,那么它一定可以在可能发生的“上下文切换”之前(切换到其他线程执行)执行完毕。原子性可以应用于除long和double之外的所有基本类型之上的“简单操作”。对读取和写入除long和double之外的基本类型变量这样的操作,可以保证他们会被当做不可分(原子)的操作来执行内存。但是JVM可以将64位(long和double变量)的读取和写入当做两个分离的32位操作来执行,这就产生了一个在读取和写入操作中间发生上下文切换,从而导致不同的任务看到不正确结果的可能性。但是,如果使用volatile关键字,就会获得(简单的赋值与返回操作的)原子性。因此,原子操作在一定程度和角度而言,可由线程机制(非代码)来保证其不可中断,但这仍是不被建议的。依赖于原子性是很棘手和很危险的,我们要知道,在java.util.consurrent类库中已经实现了某些更加巧妙的构建。我们要坚决抵制完全依赖自己的能力去处理的欲望。
​ 其次,volatile主要还是保证可视性。如果你将一个域声明为volatile,那么只要对这个域产生了写操作,那么所有的读操作就都可以看到这个修改。即便使用了本地缓存,情况还是如此,volatile域会被立即写入到主存中,而读取操作就发生在主存中。顺便提一句,可视性还可以由同步来保证,同步也会导致主存刷新,因此如果一个域完全是由synchronized方法或语句块来防护,那就不必将其设置为volatile。
​ 在某些情况下,由volatile保证可视性可能会失败。比如一个值依赖于它之前的值(例如递增一个计数器),volatile就无法工作了。如果某个值受到其他域的值得限制,那么volatile也无法工作,例如Range类的lower和upper边界就必须遵循lower<=upper的限制。
​ 补充说明,在Java中什么才属于原子操作呢?对域中的值做赋值和返回操作的简单操作(不包含i++)通常都是原子性的。
​ 基本上,如果一个域可能会被多个任务同时访问,或者这些任务中至少有一个是写入任务,那么,你就应该将这个域设置为volatile的,如果讲一个域定义为volatile,那么它就会告诉编译器,不要执行任何移除读取和写入操作的优化,这些操作的目的使用线程中的局部变量维护对这个域的精确同步

原子类

原子性变量类:AtomicInteger,AtomicLong,AtomicReference…

原子性条件更新操作:

boolean compareAndSet(expectedValue,updateValue);

使用AtomicInteger优化代码示例:

​ 如果不使用AtomicInteger类,而是使用int类型,那么AtomicIntegerTest.getValue()及AtomicIntegerTest.evenIncrement()都必须加锁。

image-20210902172950483

设计模式之模板方法:

image-20210902174117628

image-20210902174026549

在其他对象上同步

下面的示例演示了两个任务可以同时进入同一个对象,只要这个对象上的方法是在不同的锁上同步即可:
image-20210902174617878

**从输出可以看到,任何一个方法都没有因为对另一个方法的同步而阻塞。**

防止任务在共享资源上产生冲突的第二种方式:线程本地存储

ThreadLocal特性

ThreadLocal和Synchronized都是为了解决多线程中相同变量的访问冲突问题,不同的点是

  • Synchronized是通过线程等待,牺牲时间来解决访问冲突
  • ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。

线程本地存储可以为使用相同变量的的每个不同线程都创建不同的存储,这种方式就根除了对变量的共享,从而防止任务在共享资源上产生冲突。

image-20210903092259113

将线程与状态关联起来图示

代码示例:

package com.lxy.二刷Java编程思想.共享受限资源.线程本地存储;

/**
 * @author luxiaoyang
 * @create 2021-09-03-9:28
 */
public class A implements Runnable {
    private final int id;

    public A(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            ThreadLocalVariableHolder.increment();
            System.out.println(this);
            Thread.yield();
        }
    }

    @Override
    public String toString() {
        return "A{" +
                "id=" + id + "  thlocal: " + ThreadLocalVariableHolder.getV() +
                '}';
    }
}


package com.lxy.二刷Java编程思想.共享受限资源.线程本地存储;

import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author luxiaoyang
 * @create 2021-09-03-9:29
 */
public class ThreadLocalVariableHolder {
    private static ThreadLocal<Integer> v = new ThreadLocal<Integer>(){
        private Random rand = new Random(47);
        @Override
        protected synchronized Integer initialValue() {
            return rand.nextInt(10000);
        }
    };

    public static Integer getV() {
        return v.get();
    }

    public static void increment() {
        v.set(v.get() + 1);
    }

    public static void main(String[] args) throws Exception {
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            exec.execute(new A(i));
        }
        TimeUnit.MILLISECONDS.sleep(1);
        exec.shutdownNow();
    }

}

输出:

image-20210903094138426

可以看到,每个线程的的变量都与线程关联了起来。

总结:
  • ​ ThreadLocal对象通常当做静态域存储。
  • ​ 当创建ThreadLocal时,你只能通过get()和set()方法来访问对象的内容。
  • ​ get()方法将返回与其线程相关联的对象的副本,而set()会将参数插入到为其咸亨存储的对象中,并返回存储中原有的对象。
  • ​ increment()和getV()方法都不是synchronized的,因为ThreadLocal保证不会出现竞争条件。
  • ​ 每个线程都被分配了自己的存储,因为他们每个都需要跟踪自己的计数值,即便只有一个ThreadLocalVatiableHolder对象。
ThreadLocal源码阅读:

​ ThreadLocal类提供了变量的线程本地存储。这些变量与普通变量的不同之处在于,每个访问一个变量的线程都有自己的、独立初始化的变量副本。实例通常是希望将状态与线程关联的类中的私有静态字段。

​ 每个线程都持有一个对线程局部变量副本的隐式引用,只要线程是活的并且实例是可访问的;当一个线程离开后,它的所有线程本地实例副本都将被垃圾收集。

​ 从表面上看ThreadLocal相当于维护了一个map,key就是当前的线程,value就是需要存储的对象。这里的这个比喻是不恰当的,实际上是ThreadLocal的静态内部类ThreadLocalMap为每个Thread都维护了一个数组table,ThreadLocal确定了一个数组下标,而这个下标就是value存储的对应位置。

ThreadLocal初始化当前线程本地存储变量值的两种方式:
方式一:重写initialValue()
 private static ThreadLocal<Integer> v = new ThreadLocal<Integer>() {
        Random rand = new Random(47);

        @Override
        protected synchronized Integer initialValue() {
            return rand.nextInt(10000);
        }
    };
方式二:调用withInitial()
private static ThreadLocal<Integer> v = ThreadLocal.withInitial(() -> MySupplier.rand.nextInt(10000));

/**
 * @author luxiaoyang
 * @create 2021-09-03-10:52
 */
public interface MySupplier<T> extends Supplier {
    Random rand = new Random(47);

    @Override
    public Object get();
}

// 上面MySupplier extends Supplier 也可以不继承,直接写
  public EventLoopGroupMetrics(Registry registry)
    {
        this.registry = registry;
        this.metricsForCurrentThread = ThreadLocal.withInitial(() ->
        {
            String name = nameForCurrentEventLoop();
            EventLoopMetrics metrics = new EventLoopMetrics(registry, name);
            byEventLoop.put(Thread.currentThread(), metrics);
            return metrics;
        });
    }
关于调用withInitial()初始化值的一点解读:

源码:

   /**
     * An extension of ThreadLocal that obtains its initial value from
     * the specified {@code Supplier}.
     */
    static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {

        private final Supplier<? extends T> supplier;

        SuppliedThreadLocal(Supplier<? extends T> supplier) {
            this.supplier = Objects.requireNonNull(supplier);
        }

        @Override
        protected T initialValue() {
            return supplier.get();
        }
    }

SuppliedThreadLocal持有一个不可变supplier,由构造器初始化,重写了ThreadLocal.initialValue()方法,返回supplier.get()

ThreadLocal提供了withInitial()方法,来创建一个SuppliedThreadLocal实例

public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
    return new SuppliedThreadLocal<>(supplier);
}

get():

 public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

ThreadLocal中维护了一个ThreadLocalMap,以当前线程(t)为key。

ThreadLocal的应用场景

一:

ThreadLocal给每个线程维护一个自己的simpleDateFormat对象,这个对象在线程之间是独立的,互相没有关系的。这也就避免了线程安全问题。与此同时,simpleDateFormat对象还不会创造过多,线程池一共只有 16 个线程,所以需要16个对象即可

public class ThreadLocalDemo04 {

    public static ExecutorService threadPool = Executors.newFixedThreadPool(16);

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

        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            threadPool.submit(() -> {
                String data = new ThreadLocalDemo04().date(finalI);
                System.out.println(data);
            });
        }
        threadPool.shutdown();
    }

    private String date(int seconds){
        Date date = new Date(1000 * seconds);
        SimpleDateFormat dateFormat = ThreadSafeFormater.dateFormatThreadLocal.get();
        return dateFormat.format(date);
    }
}

class ThreadSafeFormater{
    public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("mm:ss"));
}

二:

每个线程内需要保存类似于全局变量的信息(例如在拦截器中获取的用户信息),可以让不同方法直接使用,避免参数传递的麻烦却不想被多线程共享(因为不同线程获取到的用户信息不一样)。

例如,用 ThreadLocal 保存一些业务内容(用户权限信息、从用户系统获取到的用户名、用户ID 等),这些信息在同一个线程内相同,但是不同的线程使用的业务内容是不相同的。

package com.lxy.二刷Java编程思想.共享受限资源.线程本地存储;

public class ThreadLocalDemo05 {
    public static void main(String[] args) {
        User user = new User("jack");
        new Service1().service1(user);
    }

}

class Service1 {
    public void service1(User user) {
        //给ThreadLocal赋值,后续的服务直接通过ThreadLocal获取就行了。
        UserContextHolder.holder.set(user);
        new Service2().service2();
    }
}

class Service2 {
    public void service2() {
        User user = UserContextHolder.holder.get();
        System.out.println("service2拿到的用户:" + user.name);
        new Service3().service3();
    }
}

class Service3 {
    public void service3() {
        User user = UserContextHolder.holder.get();
        System.out.println("service3拿到的用户:" + user.name);
        //在整个流程执行完毕后,一定要执行remove
        UserContextHolder.holder.remove();
    }
}

class UserContextHolder {
    //创建ThreadLocal保存User对象
    public static ThreadLocal<User> holder = new ThreadLocal<>();
}

class User {
    String name;

    public User(String name) {
        this.name = name;
    }
}

终结任务

线程状态:

  • ​ 新建
  • ​ 就绪
  • ​ 阻塞
    • sleep()
    • wait()
    • I/O阻塞
    • 同步阻塞
  • ​ 死亡

如何终止处于阻塞状态的任务,如果对于处于阻塞状态的任务,你不能等待其到达代码中可以检查其状态值的某一点,因而决定让它主动的终止,难么你就必须强制这个任务跳出阻塞状态

当你打断被阻塞的任务时,可能需要清理资源。正因为这一点,在任务的run()方法中间打断,更像是抛出异常,因此在Java线程中这种类型的中断使用到了异常,为了在以这种方式终止任务时,返回良好状态,必须仔细考虑代码的执行路径,并仔细编写catch子句以正确清楚事务。

image-20210903150134068

线程之间的协作

主要就是wait(),notify(),notigyAll(),await(),signal(),singalAll().以及一些阻塞队列。

新类库中的构建

CountDownLatch

闭锁特性:

  • 被用来同步一个或多个任务,强制他们等待由其他任务执行的一组操作完成。
  • 可以向CountDownLatch对象设置一个初始计数值,任何在这个对象上调用wait()的方法都将阻塞,直到这个计数值到达0
  • 其他任务在结束其工作时,可以在该对象上调用countDown()来减小这个计数值。
  • CountDownLatch被设计为只触发一次,计数值不能被重置,如果需要能够重置计数值的版本,则可以使用CyclicBarrier。

应用场景:

​ CountDownLatch的典型用法是将一个程序分为n个互相独立的可解决任务,并创建值为0的CountDownLatch。当每个任务完成时,都会在这个锁存器上调用countDown().等待问题被解决的任务在这个锁存器上调用await(),将他们自己拦住,知道锁存器计数结束。

package com.lxy.心类库中的构建.闭锁;

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author luxiaoyang
 * @create 2021-08-25-10:34
 */
public class CountDownLatchDemo {
    static final int SIZE = 100;

    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        CountDownLatch latch = new CountDownLatch(SIZE);
        for (int i = 0; i < 10; i++) {
            exec.execute(new WaitingTask(latch));
        }
        for (int i = 0; i < SIZE; i++) {
            exec.execute(new TaskPortion(latch));
        }
        System.out.println("launched all task");
        // 当所有任务完成的时候退出
        exec.shutdown();
    }

}

class TaskPortion implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private Random rand = new Random(47);
    private final CountDownLatch downLatch;

    public TaskPortion(CountDownLatch downLatch) {
        this.downLatch = downLatch;
    }

    @Override
    public void run() {
        try {
            doSomething();
            downLatch.countDown();
        } catch (InterruptedException e) {
            System.out.println("TaskPortion interrupted");
        }
    }

    private void doSomething() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(rand.nextInt());
        System.out.println("TaskPortion completed");
    }

    @Override
    public String toString() {
        return "TaskPortion{" +
                "id=" + id +
                '}';
    }
}

class WaitingTask implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private CountDownLatch countDownLatch;

    public WaitingTask(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        try {
            countDownLatch.await();
            System.out.println("Latch barrier pass for" + this);
        } catch (InterruptedException e) {

        }
    }

    @Override
    public String toString() {
        return "WaitingTask{" +
                "id=" + id +
                '}';
    }
}

CyclicBarrier

可以向CyclicBarrier提供一个”栅栏动作“,它是一个Runnable,当计数值达到0时自动执行。

package com.lxy.心类库中的构建.回环栅栏;

import javax.xml.ws.Holder;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

/**
 * @author luxiaoyang
 * @create 2021-08-25-17:08
 */
public class HorseRace {
    static final int FINISH_LINE = 75;
    private List<Horse> horses = new ArrayList<>();
    private ExecutorService exec = Executors.newCachedThreadPool();
    private CyclicBarrier barrier;

    public HorseRace(int nHorses,final int pause) {
        barrier = new CyclicBarrier(nHorses, new Runnable() {
            @Override
            public void run() {
                StringBuilder s = new StringBuilder();
                for (int i = 0; i < FINISH_LINE; i++) {
                    // 赛马场的栅栏
                    s.append("=");
                }
                System.out.println(s);
                for (Horse horse : horses) {
                    System.out.println(horse.tracks());
                }
                for (Horse horse : horses) {
                    if(horse.getStrides() >= FINISH_LINE) {
                        System.out.println(horse + "won!");
                        exec.shutdownNow();
                        return;
                    }
                }
                try {
                    System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
                    TimeUnit.MILLISECONDS.sleep(pause);
                } catch (InterruptedException e) {
                    System.out.println("barrier-action sleep interrupted");
                }
            }
        });
        for (int i = 0; i < nHorses; i++) {
            Horse horse = new Horse(barrier);
            horses.add(horse);
            exec.execute(horse);
        }
    }

    public static void main(String[] args) {
        int nHorse = 7;
        int pause = 200;
        if(args.length > 0) {
            Integer n = new Integer(args[0]);
            nHorse = n > 0 ? n : nHorse;
        }
        if(args.length > 1) {
            Integer p = new Integer(args[1]);
            pause = p > -1 ? p : pause;
        }
        new HorseRace(nHorse,pause);
    }
}

class Horse implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private int strides = 0;
    private static Random rand = new Random(47);
    private static CyclicBarrier barrier;

    public Horse(CyclicBarrier b) {barrier = b;}

    public synchronized int getStrides() {return strides;}

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                synchronized (this) {
                    // produce 0 1 or 2
                    System.out.println(Thread.currentThread().getName() + ">>>>>>>>>>>>>");
                    strides += rand.nextInt(3);
                }
                barrier.await();
            }
        } catch (InterruptedException e) {

        } catch (BrokenBarrierException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String toString() {
        return "Horse" + id + " ";
    }

    public String tracks() {
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < getStrides(); i++) {
            s.append("*");
        }
        s.append(id);
        return s.toString();
    }
}

Semaphore

​ 计数信号量允许n个任务同时访问一项资源。

package com.lxy.心类库中的构建.信号量;

/**
 * @author luxiaoyang
 * @create 2021-08-27-15:47
 */
public class Fat {
    // 阻止编译器优化
    private volatile double d;

    private static int counter = 0;
    private final int id = counter++;

    public Fat(double d) {
        for (int i = 0; i < 10000; i++) {
            d += (Math.PI + Math.E) / (double)i;
        }
    }

    public void operation() {
        System.out.println(this);
    }

    @Override
    public String toString() {
        return "Fat id: " + id;
    }
}
package com.lxy.心类库中的构建.信号量;

import java.util.concurrent.TimeUnit;

/**
 * @author luxiaoyang
 * @create 2021-08-27-15:51
 */
public class CheckOutTask<T> implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private Pool<T> pool;

    public CheckOutTask(Pool<T> pool) {
        this.pool = pool;
    }

    @Override
    public void run() {
        try {
            T item = pool.checkOut();
            System.out.println(this + "checked out " + item);
            TimeUnit.SECONDS.sleep(1);
            System.out.println(this + "checked in " + item);
            pool.checkIn(item);
        } catch (InterruptedException e) {

        }
    }

    @Override
    public String toString() {
        return "CheckoutTask " + id + " ";
    }
}
package com.lxy.心类库中的构建.信号量;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

/**
 * @author luxiaoyang
 * @create 2021-08-27-15:22
 */
public class Pool<T> {
    private int size;
    private List<T> items = new ArrayList<>();
    private volatile boolean[] checkedOut;
    private Semaphore available;

    public Pool(Class<T> classObject, int size) {
        this.size = size;
        checkedOut = new boolean[size];
        available = new Semaphore(size, true);
        // load pool with objects that can be checked out
        for (int i = 0; i < size; i++) {
            try {
                items.add(classObject.newInstance());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public T checkOut() throws InterruptedException {
        available.acquire();
        return getItem();
    }

    public void checkIn(T x) {
        if (releaseItem(x)) {
            // Releases a permit, returning it to the semaphore.
            available.release();
        }
    }

    private synchronized T getItem() {
        for (int i = 0; i < size; ++i) {
            if (!checkedOut[i]) {
                checkedOut[i] = true;
                return items.get(i);
            }
        }
        // Semaphore 将会阻止程序到达这里!
        return null;
    }

    private synchronized boolean releaseItem(T item) {
        int index = items.indexOf(item);
        if (index == -1) {
            return false;
        }
        if (checkedOut[index]) {
            checkedOut[index] = false;
            return true;
        }

        // 没被签发
        return false;
    }
}
package com.lxy.心类库中的构建.信号量;

import java.security.Policy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author luxiaoyang
 * @create 2021-08-27-15:56
 */
public class SemaphoreDemo {
    final static int SIZE = 25;

    public static void main(String[] args) throws Exception {
        final Pool<Fat> pool = new Pool<>(Fat.class, SIZE);
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < SIZE; i++) {
            exec.execute(new CheckOutTask<Fat>(pool));
        }
        System.out.println("All CheckOutTask created");
        List<Fat> list = new ArrayList<>();
        for (int i = 0; i < SIZE; i++) {
            Fat f = pool.checkOut();
            System.out.println(i + ": main() thread checked out");
            f.operation();
            list.add(f);
        }
        Future<?> blocked = exec.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    // semaphore prevents additional checkout,so call is blocked
                    pool.checkOut();
                } catch (InterruptedException e) {
                    System.out.println("checkOut() Interrupted");
                }
            }
        });

        TimeUnit.SECONDS.sleep(2);
        // break out of blocked call
        blocked.cancel(true);
        System.out.println("Checking in objects in " + list);
        for (Fat f : list) {
            pool.checkIn(f);
        }
        // second check in ignore
        for (Fat f : list) {
            pool.checkIn(f);
        }
        exec.shutdown();
    }
}
    }
    System.out.println("All CheckOutTask created");
    List<Fat> list = new ArrayList<>();
    for (int i = 0; i < SIZE; i++) {
        Fat f = pool.checkOut();
        System.out.println(i + ": main() thread checked out");
        f.operation();
        list.add(f);
    }
    Future<?> blocked = exec.submit(new Runnable() {
        @Override
        public void run() {
            try {
                // semaphore prevents additional checkout,so call is blocked
                pool.checkOut();
            } catch (InterruptedException e) {
                System.out.println("checkOut() Interrupted");
            }
        }
    });

    TimeUnit.SECONDS.sleep(2);
    // break out of blocked call
    blocked.cancel(true);
    System.out.println("Checking in objects in " + list);
    for (Fat f : list) {
        pool.checkIn(f);
    }
    // second check in ignore
    for (Fat f : list) {
        pool.checkIn(f);
    }
    exec.shutdown();
}

}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Redis是一种内存数据库,可以用于实现草稿箱功能。下面是一个使用Redis实现草稿箱的简单示例: 1. 连接到Redis:首先,你需要通过Java Redis客户端连接到Redis数据库。例如,可以使用Jedis或Lettuce等流行的Redis客户端库。 2. 保存草稿:当用户选择保存内容为草稿时,将草稿的内容存储在Redis中。可以使用哈希表(Hash)来表示每个草稿,其中键是草稿的唯一标识符,而字段和值可以表示草稿的各个属性,如标题、内容和创建时间等。 ```java Jedis jedis = new Jedis("localhost", 6379); String draftId = "draft:123"; Map<String, String> draftData = new HashMap<>(); draftData.put("title", "My Draft"); draftData.put("content", "This is my draft content"); draftData.put("created", "2021-10-01"); jedis.hmset(draftId, draftData); ``` 3. 获取草稿:当用户需要编辑草稿时,通过草稿的唯一标识符从Redis中获取草稿的详细信息。 ```java Map<String, String> draftData = jedis.hgetAll(draftId); String title = draftData.get("title"); String content = draftData.get("content"); // 显示在编辑界面供用户修改 ``` 4. 更新草稿:当用户对草稿进行修改后,更新Redis中对应草稿的内容。 ```java Map<String, String> updatedData = new HashMap<>(); updatedData.put("title", "Updated Draft"); updatedData.put("content", "This is the updated draft content"); jedis.hmset(draftId, updatedData); ``` 5. 删除草稿:如果用户决定删除草稿,从Redis中删除对应的草稿数据。 ```java jedis.del(draftId); ``` 需要注意的是,上述示例只提供了基本的操作,实际应用中可能还需要考虑并发访问、草稿列表的管理、过期时间设置等其他方面的功能。此外,你还可以根据具体需求添加其他字段或操作来扩展草稿功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赞一下鼓励

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值