读书笔记-Java并发编程从入门到精通-张振华

Java并发编程从入门到精通 张振华 清华大学出版社 ISBN-9787302401919 

仅供参考, 自建索引, 以备后查

=================================================================

一: 

CPU型号

# cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c

CPU核心数

# cat /proc/cpuinfo | grep 'core id' | sort -u | wc -l

CPU线程数

# cat /proc/cpuinfo | grep 'processor' | sort -u | wc -l

 

进程   线程

箱子     箱子里面的球

PCB     TCB

 

并行: 线程小于CPU支持的最大数量, 线程同时运行

并发: 线程大于CPU支持的最大数量, 时间片轮转进程调度, 线程轮番执行

当线程数量大数CPU支持的最大数量时, 并行->并发

分布式: 多台机器分别计算, 最后结果合并

 

多线程, 高性能Web前端调优, 代码模块化, 异步化

线程安全, 线程死锁, 线程过多切换过头, 线程池

 

 

=================================================================

二: Java Thread

线程实现  

class A extends Thread       

    new A().start();

class B implements Runnable  

    new Thread(new B()).start();

class C implements Callable<String>

    new Thread(new FutureTask<String>(new C())).start();

 

优先级: MIN_PRIORITY=1 NORM_PRIORITY=5 MAX_PRIORITY=10

生命周期: new runnable running blocked dead

 

Thread.currentThread().getThreadGroup();

 

ThreadLocal 拷贝创建当前线程数据副本 

内部使用 ThreadLocalMap<Thread, V>  线程对象作Key,线程安全

 

unchecked exception 可以通过 setUncaughtExceptionHandler(..) 捕获

 

 

=================================================================

三: Thread安全

CPU  寄存器->高速缓存->内存

原始数据在内存  计算时拷贝到 寄存器或高速缓存   之后写回内存

对于共享变量就存在了多线程并发问题

线程不安全: 仅仅只是针对多个线程访问同一个变量才会有机会发生

    如: 访问单例对象, 未使用锁, 未使用线程安全的类库

线程之间没有交互,没有共享变量,就不会发生,即线程安全

 

对象方法锁 F    

public synchronized void method1(..) {} 

代码块锁 D 

public void method2(..) { synchronized(obj) {} } 

变量高效锁 B

private byte[] lock3 = new byte[1];

public void method3(..){ synchronized(lock3) {} }

显示锁 

public interface Lock {}

public class ReentrantLock implements Lock {}

class C {

    private final Lock lock = new ReentrantLock();

    private void methodC(Object... args) {

        lock.lock();

        try {  
            // TODO
        } 
        finally { 
            lock.unlock(); 
        }

    }

}

 

读写锁

public interface ReadWriteLock {}

public class ReentrantReadWriteLock implements ReadWriteLock {}

ReadWriteLock: 读读不互斥, 读写互斥, 写写互斥, 高并发时效率超高

 

public class StampedLock {}

支持乐观悲观方式读写 JDK1.8

 

volatile [ˈvɒlətaɪl]  

public volatile static int count = 0;

 

java.util.concurrent.atomic 原子操作

AtomicInteger, AtomicLong, AtomicBoolean

AtomicReference, AtomicIntegerArray, AtomicReferenceArray

AtomicIntegerFieldUpdater, AtomicReferenceFieldUpdater

Java 中 atomic 原子操作是利用 CPU 比较并交换 CAS: Compare and Swap

  使用 JNI: Java Native Interface 调用 C 执行 CPU 底层指令       

 

单例模式线程安全性能高的两种写法

class One {

    private static One one;

    private static byte[] lock = new byte[1];

    private One() {}

    public static One getInstance() {

        if (one == null) {

            synchronized(lock) {

                if (one == null) one = new One();

            }

        }

        return one;

    }

}
class Two {

    private static Two two;

    private static ReentrantLock lock = new ReentrantLock();

    private Two() {}

    public static Two getInstance() {

        if (two == null) {

            lock.lock();

            if (two == null) two = new Two();

            lock.unlock();

        }

        return two;

    }

}

 

 

=================================================================

四: 线程安全的集合类

Collection—>Set/List/Queue

Map—>ConcurrentMap/SortedMap

 

Hashtable  

public synchronized .. ..(..) {}

 

ConcurrentHashMap 内部使用 ReentrantLock 

 

CopyOnWriteArrayList 内部使用 ReentrantLock

线程安全,读操作远远大于写操作时性能超级高,高并发性能强,不受多线程并发问题影响

CopyOnWriteArraySet 使用 CopyOnWriteArrayList

 

CopyOnWrite 任意读,修改时读出数据到新对象,修改数据再加锁使原对象指向新对象

 

StringBuffer 线程安全, 使用synchronized加锁

StringBuilder 线程不安全效率高

 

 

=================================================================

五: 多线程交互

Queue 队列 FIFO  

Deque 双端队列 

BlockingQueue 阻塞队列  add/offer/put   remove/poll/take   element/peek

 

ArrayBlockingQueue 

LinkedBlockingQueue 生产锁 消费锁 高并发性能高

PriorityBlockingQueue 不阻塞生产者 

DelayQueue 可用于去除到期数据, 或者任务调试   使用时需要实现 Delayed 接口

SynchronousQueue 生产消费一对一传递,吞吐量高于 [Linked/Array]BlockingQueue

 

LinkedBlockingDeque 链表双向阻塞队列

 

LinkedTransferQueue 生产的对象可能不入列就交给消费者了,效率更高,具有异步特性

 

AbstractQueuedSynchronized 抽象对列同步器

 

CountDownLatch 同步计数器

void countDown(); 计数减一

void await(); 阻塞到计数为零

    例: 当前线程启动N个子线程后开始等待, 等到所有子线程执行完毕再开始执行

    注: 所有子线程必须拥有的相同的CountDownLatch对象

    注: 设定数量必须等于开始的线程数量

 

Semaphore [ˈseməfɔ:(r)] 同步计数器

void acquire(); 获取信号开始阻塞

void release(); 释放信号开始执行

    例: 有100个文件需要上传,使用Semaphore设置信号量为3,最大同时允许3个线程支行

    注: 及时调用release()释放,不然会一直等待

    注: 所有子线程必须拥有的相同的Semaphore对象

 

CyclicBarrier [ˈsaɪklɪk] [ˈbæriə(r)] 同步计数器

int getParties(); 阻塞线程的计数器

int await();  线程阻塞,直到调用await()次数==getParties()返回值,所有线程向下执行

    例: 评委们先后确定最终投票人,但是必须等到最后一名评委确定人选都能开始统计

    注: 所有子线程必须拥有的相同的CyclicBarrier对象

    注: await()调用次数必须等于getParites()返回值

 

 

=================================================================

六: 线程池

Executor—>ExecutorService

 

Executors

  newSingleThreadExecutor     LinkedBlockingQueue

  newFixedThreadPool             LinkedBlockingQueue

  newCachedThreadPool         SynchronousQueue

  newWorkStealingPool            ForkJoinPool

 

线程完成任务时间可分为 

 T1: 创建线程消耗

 T2: 任务执行

 T3: 销毁线程

其中T2的执行时间与业务相关,相对固定.  线程池在T1与T3阶段优化执行效率

 

工作原理:

  核心队列: 等待执行队列 BlockingQueue     任务执行队列: HashSet<Worker>

  corePoolSize: 线程池中最少线程数量         maximumPoolSize: 最大处理线程数

 

public interface ThreadFactory {} 线程工厂

 

public interface RejectedExecutionHandler {} 线程饱和对列已满后的丢弃处理

 

ThreadPoolExecutor 线程池定义 <<<<=========

AbortPolicy 直接抛出RejectedExecutionException异常

CallerRunsPolicy 回到调用线程的父线程执行,要是父线程已死则丢弃

DiscardOldestPolicy 丢弃最早入列未执行的线程

DiscardPolicy 丢弃

 

		ExecutorService es = Executors.newCachedThreadPool();
		Stream.iterate(1, s->s+1).limit(100).forEach(i->{
			es.execute(()->{
				System.out.println("Thread-"+i + "--------");
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("Thread-"+i + "<<<<<<<<");
			});
		});
		es.shutdown();
		/// 如果需要等待线程池所有任务执行完毕再往下执行
		while ( ! es.isTerminated()) {
		}

 

注意事项:

线程池使用单例, 不用单例模式就是创建N个线程池了不是吗??

线程池数量大小

线程中注意别出现死锁代码

 

 

=================================================================

七: Fork/Join

接口

public interface Runnable {}

public interface Future<V> {}

public interface Callable<V> {}

public interface RunnableFuture<V> extends Runnable, Future<V> {}

实现类

public class FutureTask<V> implements RunnableFuture<V> { }

使用方法

new Thread(new FutureTask<String>(new Callable<String>())).start();

调用 Future get() 方法会阻塞线程等待执行完毕返回结果

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

public class CallableRunnableFutureTaskDemo1 {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		/// Callable 实例
		LongTimeWorkCallable cc = new LongTimeWorkCallable();
		FutureTask<String> f1 = new FutureTask<String>(cc);
		/// Runnable 实例
		DaemonWorkRunnable dd = new DaemonWorkRunnable();
		FutureTask<Integer> f2 = new FutureTask<Integer>(dd, 111);
		/// 开始执行
		new Thread(f1).start();
		new Thread(f2).start();
		/// 结果获取
		System.out.println(f1.get());
		System.out.println(f2.get()); /// 返回值为 111, 调用时传递的预定义返回结果
		
	}
}
class DaemonWorkRunnable implements Runnable {
	public void run() {
		System.out.println("----Deamon work launchs");
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("----Deamon work accomplishes");
	}
}
class LongTimeWorkCallable implements Callable<String> {
	public String call() throws InterruptedException {
		System.out.println("-----Work starts...");
		Thread.sleep(5000);
		System.out.println("-----Work completes");
		return "Work-100%";
	}
}

 执行后的控制台显示内容, 两个线程开始执行, 但是调用 get 方法有先后, 若先调用的线程执行时间较长, 则会阻塞到这一线程执行完毕

-----Work starts...
----Deamon work launchs
----Deamon work accomplishes
-----Work completes
Work-100%
111

 若把上面的打印语句的顺序调换一下

		/// 结果获取
		System.out.println(f2.get()); /// 返回值为 111, 调用时传递的预定义返回结果
		System.out.println(f1.get());

则控制台会打印, 因为 f2 执行消耗时间短, 所以会先打印, 再等待 f1 执行完成 

-----Work starts...
----Deamon work launchs
----Deamon work accomplishes
111
-----Work completes
Work-100%

 

Fork/Join框架   Map/Reduce框架

fork()  执行切割后的数据运算

join()  对fork()结果进行合并

map() 对Datanode节点的数据进行运算之后Combine

reduce() 对map()结果进行合并

public abstract class ForkJoinTask<V> implements Future<V>, Serializable {}

public abstract class RecursiveAction extends ForkJoinTask<Void> {} 无返回结果

public abstract class RecursiveTask<V> extends ForkJoinTask<V> {} 有返回值

执行 Fork/Join 任务需要 ForkJoinPool 线程池

public abstract class AbstractExecutorService implements ExecutorService {}

public class ForkJoinPool extends AbstractExecutorService {} 

    public void execute(..) {}  异步执行

    public ForkJoinTask<?> submit(..) {}  返回Future对象

继承 RecursiveTask / RecursiveAction 并实现 protected abstract V compute(); 进行数据切割

work-stealing 工作窃取算法

ForkJoinTask  public final boolean isCompletedAbnormally() {} 线程执行中是否出现异常

 

 

=================================================================

八: WEB项目线程线程池的应用

Servlet 

void init(ServletConfig config);

void service(ServletRequest req, ServletResponse rep);

void destroy();

 

Tomcat 可选 线程池 在 server.xml 修改

<Executor name="executor1Name" .../>

<Connector executor="executor1Name" .../>

 

public abstract interface org.apache.catalina.Executor extends java.util.concurrent.Executor, org.apache.catalina.Lifecycle {}

实现 Executor 可以自定义可在 Tomcat 中使用的线程池

 

Nginx线程池相关介绍

 

数据库连接池介绍

com.alibaba.druid.pool.DruidDataSource

CountDownLatch, AtomicLong, ReentrantLock, volatile

 

如何在分布式系统中实现高并发: 了解机器 业务 程序 开始拆分

  • 任务性质: CPU计算型任务, IO密集型任务, 内存消耗型
  • 任务并发数量: 超大并发, 大并发, 中等, 很少
  • 任务执行时间: 长耗时, 中等, 很少
  • 任务优先级: 高, 中, 低
  • 任务依赖性: 是否依赖数据库连接, 业务之间是否相互调用依赖
  • 资源性质: 静态资源, 动态资源
  • 业务是否耦合: 高耦合, 可解耦, 无耦合

部署分离:  

  • 渠道分享: 后端, 前端, APP端, API接口
  • 运营商: 电信, 联通, 教育, 海外服务
  • 服务内容: 文字, 图片, 视频, 下载服务
  • 访问密集性: 高并发, 低并发分别布置

 

=================================================================

九: 线程监控

public class ThreadPoolExecutor extends AbstractExecutorService {

    protected void beforeExecute(Thread t, Runnable r) { }
    protected void afterExecute(Runnable r, Throwable t) { }
    protected void terminated() { }

}

继承 ThreadPoolExecutor 类, 重写以上三个方法, 可以在线程执行前, 执行后和线程池关闭后

taskCount  completedTaskCount  largestPoolSize  getPoolSize  getActiveCount

ForkJoinPool 监控通过拥有的方法进行查阅

 

Java内存结构

GC: Garbage Collector

JVM:  程序计数器  JVM栈  本地方法栈  共享堆  方法区

程序计数器: 当前线程执行字节码的行号指示器. 线程私有内存, 唯一一个没有内存溢出的区域

JVM栈: 线程私有, 方法执行局部变量表, 操作数栈, 动态链接和方法出口信息. 方法入栈出栈

本地方法栈: Java调用操作系统本地方法服务

共享堆: 所有线程共享内存区域, 为了有些话对象实例. 新生代和年老代

方法区: 所有线程共享, 存储被虚拟机加载的 类信息, 常量, 静态变量和即时编译后的代码

垃圾回收机制: 

Permanent Generation: 对应方法区, 几乎不参与垃圾回收

Young Generation: Eden(minorGC), Survior, Survior 

Old Generation: MajorGC

FullGC 对整个堆内存进行垃圾回收

串行收集器: 单线程垃圾回收

并行收集器: 对年轻代进行垃圾回收, 多线程多处理器上使用

并发收集器: 适用大中高型大规模应用

内存溢出: 

JVM Heap堆内存溢出 -Xmn -Xms -Xmx

PermGen space内存溢出 存放 Class 对象及 Meta 信息 -XX:PermSize -XX:MaxPermSize

java.lang.StackOverflowError 栈溢出 -Xss 设置线程内存大小

Server JVM参数设置

JAVA_OPTS="-Xms4g -Xms4g -Xmn1024m -XX:PermSize=512m -XX:MaxPermSize=512m -XX:SurvivorRatio=6"

 

Java运行状况可视化监控工具

VisualVM

VisualVM+JTA插件

JConsole

Oracle Java Mission Control     jmc.exe

 

Linux线程分析监控

top

JAVA_HOME/bin/jstack

 

Eclipse     Window->Preferences    Java-Debug-Default suspend policy for new breakpoints: Suspend VM

 

压力测试  Badboy+JMeter

MultithreadedTC 并发测试

 

 

=================================================================

十: Android线程

<activity/> <service/> <receiver/> <provider/> 可以指定 android:process 指定支行的线程

<Application/> 也支持 android:process

 

Android 进程重要性等级

  • 前台进程 顶级优先重要
  • 可见进程 
  • 服务进程
  • 后台进程
  • 空进程  最先回收

若前台进程或可见进程包含服务进程, 则此服务进程优先级升级到前台进程或可见进程

Activity生命周期  onCreate() onStart() onResume() onPause() onStop() onDestroy()

UI线程阻塞会出现 应用程序无响应ANR 对话框

非UI线程更新界面 

Activity.runOnUiThread(Runnable)

View.post(Runnable)

View.postDelayed(Runnable, long)

Handler

 

Android异步线程

public abstract class AsyncTask<Params, Progress, Result> {}

实现 doInBackground() 若有返回结果则需要再 onPostExecute() 方法更新UI界面

在以上两方法中可以随时 publishProgress() 更新处理进度

onPreExecute() onPostExecute() onProgressUpdate()

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值