package thread;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.StampedLock;
import java.util.stream.IntStream;

import static utils.ConcurrentUtil.sleep;
import static utils.ConcurrentUtil.stop;

/**
 * @Author frank
 * @Date 17-9-19
 *
 * Executors提供的四种线程池
 * 1 newCachedThread 可缓存线程池 如果线程池长度超过处理需求 可灵活回收空闲线程 若无可回收 则新建线程
 * 2 newFixedThreadPool 定长线程池 可控制线程最大并发数 超出的会在队列中等待
 * 3 newScheduledThreadPool 定长线程池 支持定时及周期性任务执行
 * 4 newSingleThreadExecutor 单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
 *
 * ReentrantLock 互斥锁
 *  作用类似与关键字synchronized 但更具伸缩性
 *  note:一定要释放 不然就留下了定时炸弹
 *  lock.lock();
 *  try {
 *
 *  } finally {
 *     lock.unlock();
 *  }
 *
 * ReadWriteLock 读写锁
 *  读锁 其他线程也可以读取 不能写入
 *  写锁 同步写 不能读
 *
 * StampedLock
 */
public class Main {

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

        Main main = new Main();

        main.stampedLock();
    }

    private static void runnable() {

        Runnable runnable = () -> {

            try {

                String threadName = Thread.currentThread().getName();
                System.out.println("foo : " + threadName);

                TimeUnit.SECONDS.sleep(1);

                System.out.println("bar :" + threadName);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
    }

    private static void executor() {

        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(() -> {
            String threadName = Thread.currentThread().getName();
            System.out.println("thread name : " + threadName);
        });
    }

    private static void executorShutdown() {

        ExecutorService executor = Executors.newSingleThreadExecutor();
        try {
            System.out.println("shutdown executor");
            executor.shutdown();
            executor.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            System.err.println("tasks interrupted");
        } finally {
            if (!executor.isTerminated()) {
                System.err.println("cancel non-finished tasks");
            }
            executor.shutdownNow();
            System.out.println("shutdown finished");
        }
    }

    private static void callable() throws ExecutionException, InterruptedException {

        Callable<Integer> task = () -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                return 123;
            }
            catch (InterruptedException e) {
                throw new IllegalStateException("task interrupted", e);
            }
        };

        ExecutorService executor = Executors.newFixedThreadPool(1);
        Future<Integer> future = executor.submit(task);

        System.out.println("future done? " + future.isDone());

        Integer result = future.get();

        System.out.println("future done? " + future.isDone());
        System.out.print("result: " + result);
    }

    private static void scheduled() throws InterruptedException {

        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

        Runnable task = () -> System.out.println(" Scheduling: " + System.currentTimeMillis());
        ScheduledFuture<?> future = executor.schedule(task, 3, TimeUnit.SECONDS);


        long remainingDelay = future.getDelay(TimeUnit.MILLISECONDS);
        System.out.printf("Remaining Delay: %sms", remainingDelay);
    }

    private void syn() {

        ExecutorService executor = Executors.newFixedThreadPool(2);

        IntStream.range(0, 111111).forEach(i -> executor.submit(this::incrementLock));

        stop(executor);

        System.out.println(this.count);
    }

    private void reentrantlock() {

        ExecutorService executor = Executors.newFixedThreadPool(2);
        ReentrantLock lock = new ReentrantLock();

        executor.submit(() -> {
            lock.lock();
            try {
                sleep(1);
            } finally {
                lock.unlock();
            }
        });

        executor.submit(() -> {
            System.out.println("Locked: " + lock.isLocked());
            System.out.println("Held by me: " + lock.isHeldByCurrentThread());
            boolean locked = lock.tryLock();
            System.out.println("Lock acquired: " + locked);
        });

        stop(executor);
    }

    private void readWriteLock() {

        ExecutorService executor = Executors.newFixedThreadPool(2);
        Map<String, String> map = new HashMap<>();
        ReadWriteLock lock = new ReentrantReadWriteLock();

        executor.submit(() -> {
            lock.writeLock().lock();
            try {
                sleep(1);
                map.put("foo", "bar");
            } finally {
                lock.writeLock().unlock();
            }
        });

        Runnable readTask = () -> {
            lock.readLock().lock();
            try {
                System.out.println(map.get("foo"));
                sleep(1);
            } finally {
                lock.readLock().unlock();
            }
        };

        executor.submit(readTask);
        executor.submit(readTask);

        stop(executor);
    }

    int count = 0;

    ReentrantLock lock = new ReentrantLock();

    private void increment() {
        count++;
    }

    private void incrementLock() {

        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    private synchronized void incrementSyn() {
        count++;
    }

    private void stampedLock() {

        ExecutorService executor = Executors.newFixedThreadPool(2);
        Map<String, String> map = new HashMap<>();
        StampedLock lock = new StampedLock();

        executor.submit(() -> {
            long stamp = lock.writeLock();
            try {
                sleep(1);
                map.put("foo", "bar");
            } finally {
                lock.unlockWrite(stamp);
            }
        });

        Runnable readTask = () -> {
            long stamp = lock.readLock();
            try {
                System.out.println(map.get("foo"));
                sleep(1);
            } finally {
                lock.unlockRead(stamp);
            }
        };

        executor.submit(readTask);
        executor.submit(readTask);

        stop(executor);
    }
}