高并发核心编程读书笔记(尼恩 编著)

269 阅读1分钟

买了好久了,都开始落灰了,今天决定把他拿出来学习一下,书上一下demo是可以运用于实战的,这是我第二回学习这本书了,第一回参考的是martin录的视频学习高并发的,受益良多,这次买了书,决定看书学习

学习准备

    源码下载地址:https://gitee.com/crazymaker/Java-high-concurrency-core-Programming-Volume-2-source-code
    
    

学习计划

    1. 11月25号开始,每天10页,一共460页,预计花费两个月,预计1月25号结束
    2. 每周记录一次进度
    

重点攻克

    1. P92 注册JVM钩子函数自动关闭线程池
 
    

学习记录

static class DemoThread extends Thread {

    //可以通过这种方式给线程赋线程名称
    public DemoThread() {
        super("Mall-" + threadNo++);
    }

    @Override
    public void run() {
        for (int i = 1; i < MAX_TURN; i++) {
            Print.cfo(getName() + ", 轮次:" + i);
        }
        Print.cfo(getName() + " 运行结束.");
    }
}

1. 通过继承Thread类可以实现多线程能更好的做到多个线程并发的完成各自的任务。访问各自的数据资源

2. 通过实现Runnable接口实现多线程,可以做到多个线程并发的完成同一个任务,访问同一份数据资源
这样可以将线程逻辑和业务数据进行有效分离

3. FutureTask 以 Callable入参, 实现了Runnable和 Future接口

jps -l 得到线程号

jstack pid 可打印出线程的状态

"sleepThread-3" #15 prio=5 os_prio=31 tid=0x00007f9e4a84b800 nid=0xa503 waiting on condition [0x00007000104fd000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at com.crazymakercircle.mutithread.basic.use.SleepDemo$SleepThread.run(SleepDemo.java:30)

4.线程默认不是守护的

5. 用户线程和JVM虚拟机线程、守护线程的关系
用户进程都结束了以后,JVM线程也随之终止,守护线程也终止
守护线程启动了之后再设置为守护线程会抛InterruptedException,


6. 线程进入就绪状态的几种方式
    1. 调用start方法
    2. 当前线程的执行时间片用完
    3. 线程sleep操作结束
    4. 对其他线程合入join操作结束
    5. 等待用户输入结束
    6. 线程争抢到对象锁
    7. 当前线程调用yield方法让出CPU权限

7. 线程进入阻塞或者等待的状态,CPU的时间片都会让出,但是锁会不会被释放,会根据情况而定

8.Java线程的创建非常昂贵,需要JVM和OS配合才能完成大量的工作

//统计的是线程池的工作任务数量、总计的任务数量
while (true) {
    Print.tco("- activeCount:" + executor.getActiveCount() +
            " - taskCount:" + executor.getTaskCount());
    sleepSeconds(1);
}

有两个工厂类,Executors是线程池工厂类,ThreadFactory是线程工厂类,用于创建线程

static public class SimpleThreadFactory implements ThreadFactory {
    static AtomicInteger threadNo = new AtomicInteger(1);

    //实现其唯一的创建线程方法
    @Override
    public Thread newThread(Runnable target) {
        String threadName = "simpleThread-" + threadNo.get();
        Print.tco("创建一条线程,名称为:" + threadName);
        threadNo.incrementAndGet();
        //设置线程名称
        Thread thread = new Thread(target, threadName);
        //设置为守护线程
        thread.setDaemon(true);
        return thread;
    }
}



/**
 * 自定义拒绝策略
 */
public static class CustomIgnorePolicy implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        // 可做日志记录等
        Print.tco(r + " rejected; " + " - getTaskCount: " + e.getTaskCount());
    }
}
最大线程数包含着核心工作线程数

优雅地关闭线程池:
shutDown()和shutDownNow()和awaitTermination()三个方法配合使用
shutDownNow()会stop所有线程,清空工作队列


image.png

CountDownLatch和AtomicInteger结合使用:

public static void main(String[] args) throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
    AtomicInteger atomicInteger = new AtomicInteger(0);

    ThreadPoolExecutor threadPool = ThreadUtil.getMixedTargetThreadPool();
    for (int i = 0; i < THREAD_COUNT; i++) {
        // 创建10个线程,模拟多线程环境
        threadPool.submit(() -> {
            for (int j = 0; j < 1000; j++) {
                atomicInteger.getAndIncrement();
            }
            latch.countDown();

        });
    }
    latch.await();
    Print.tco("累加之和:" + atomicInteger.get());
}

AtomicInteger 主要通过CAS‘自旋 + volatile的方案实现

LongAdder 以空间换时间,热点分离,多线程访问的时候,通过Hash算法将线程映射到数组的一个元素进行操作,将数组的元素求和就是获取最终的value结果