字符串
String 内容不可变字符串,底层用了不可变数组 final char []实现,声明不可变好处,如果声明多个相同的字符串,只会创建一个字符串对象,先会去字符串常量池中找,如果有,把对象的引用指向旧对象,旧对象丢弃
StringBuffer(线程安全), StringBuild(线程不安全,效率高) 可变字符串, char []
类初始化顺序
静态变量/静态初始化块 -》变量/初始化块 -》构造函数
继承情况下:
父类静态变量/父类静态初始化块 -》子类静态变量/子类静态初始化块 -》父类变量/父类初始化块 -》 父类构造器 -》 子类变量/子类初始化块 -》 子类构造器
静态变量和静态初始化块按照顺序加载(非静态一样)
集合
集合分为存储值(collection),和key-value(map)
值:List Set
List 有序 可重复
ArrayList 底层数组实现 ,具有索引,查询特定元素快,插入和删除慢(数组在内存中是连续的内存,删除和插入需要移动内存)
LinkedList 底层使用链表,查询时从头部开始一个一个便利索引,查询效率低,插入是不需要移动内存,只需要改变引用指向即可,所以插入删除效率高。
Set 无序 不可重复 ,根据hashcode和equals 方法判断
HashMap —- HashTable
都存储 key-value ,
1:HashMap可以存null,key或value,HashTable不可以
2: HashMap线程不安全,效率高,HashTable 线程安全,效率低
HashMap:数组加链表存储,
每个节点以Entry
如果不指定长度,默认为16,加载因子默认为0.75,那么阈值就是16*0.75 = 12,当元素HashMap中元素超过12个时,就会触发扩容,
每次扩容默认两倍,
HashMap如果在多线程的情况下发生扩容,可能会导致死循环,所以,如果多线程情况下并且必须使用HsahMap那么就在初始话的时候指定长度,或者阈值 new HashMap(20,1).
entry包含,key,vlaue ,hash,next元素组成,
-1.7 新增节点
当put时,先算出key在数组中的下标(key.hashcode() % 数组长度-1)
然后循环当前数组该下标下的所有元素链表,循环该链表,一一比对当前的key是否和链表中的key是否相同,如果相同,则替换key的value值,同时把旧的value返回。
如果链表中没有找到相同的key,那么给当前链表头部增加一个entry对象,并且当前entry对象的next指针指向链表原始头部的entry对象。
如果key为null的话,默认放到链表的第0位置
*1.8 新增节点
- 与1.7不同的是:
Entry对象该为Node对象存储; 新增的节点会放在链表的尾部; 如果链表长度达到8,会转换成红黑树存储; 扩容时,如果下标为0,那么在新的数组中下标不变,如果不为0,那么在新的数组中下标位置为扩容前的数组长度+扩容前的数组下标。
CurrentHashMap 线程安全,效率高。
1.7:原理:(把大的Map分割成N个小的Map–Segment,类似HashMap,给各个小的Map加锁,第一个Map的数据加锁时,不影响第二个Map的使用,使效率提升,默认提升16倍。)
1.8: 对数组节点的链表头部的元素加锁。链表为Node节点
字节流,字符流
字符流传递的是字符,中文汉字
字节流传输的是二进制的字节。
传输的文件比一定包含字符,有可能包含字节,比如,图片,音频,图像,为了考虑通用性,使用字节流
线程
实现方式:
1:继承Thread类
2:实现Runnable接口
java 是单继承,多实现,所以实现Runnable扩展好
启动:
Thread thread = new Thread(继承Thread的对象,或者实现Runnable的对象);
thread.start();
注意:不要用字符串常量加锁,容易产生死锁。
synchronized: 加载方法上,锁的是this,如果方法是static,锁定的是class对象,如果在执行方法是报了异常,虚拟机会自动释放锁。加锁的对象,如果对象属性改变,不影响锁的使用,如果对象的引用变了,则锁的对象发生改变,所以,synchronized锁的是堆内存的对象。保证原子性,可见性
volatile: 解决线程之间的可见性,A,B线程都读取同一个值时,如果B线程修改了值,A线程是无感知的,加上volatile时,主线程修改了值后,会强制所有线程都去重新读取值;
保证可见性,但不保证原子性;
++i 操作不保证原子性。
Atomic:简单的增加或修改数值,可以用AtomicXXX类保证原子性问题,如:(AtomicInteger,AtomicLong)
AtomicInteger a = new AtomicInteger(0);
a.incrementAndGet(); // 这个方法是替代 ++i,保证原子性
if(a.count() < 1000){
//中间不具备原子性
a.incrementAndGet();
}
wait(释放锁),object.wait(),object对象上的该线程进行等待,释放锁,其他线程可以获取object对象的锁
notify(不释放锁) 叫醒 object上等待的某个线程,notifyAll 唤醒所有等待线程
sleep(不释放锁)
测试题:两个线程,第一个线程一个自增到10,第二个线程监控,如果加到5就结束第二个线程,第一个继续往下走。
1 | package com.yahui.fan.zookeeper.ThreadTest; |
运行结果:
T2启动。。
不等于5,T2等待,释放锁
T1启动。。
add:1
add:2
add:3
add:4
add:5
等于5,唤醒T2
释放T1当前的锁,让T2执行
T2结束
T2结束,释放锁,T1继续执行
add:6
add:7
add:8
add:9
add:10
T1结束。。
1 | package com.yahui.fan.zookeeper.ThreadTest; |
运行结果:
T2开始
长度不等于5,计数器等待,T2等待。
T1开始
add:1
add:2
add:3
add:4
add:5
长度等于5,计数器减一,唤醒T2线程
T2结束。
add:6
add:7
add:8
add:9
add:10
T1结束
总结: 两种方式,第二种比较简单易懂,CountDownLatch 不需要对象锁,易扩展,没有像第一个种方式重复的释放锁,执行,过程,效率较高
ReentranLock 必须手动释放锁
tryLock() 尝试获取锁,可指定时间,获取不到锁返回false
ReentranLock lock = new ReentranLock() –非公平锁 性能高
ReentranLock lock = new ReentranLock(true); –公平锁,等待时间长的线程先执行,性能低
wait方法一般搭配while使用,不要用if
在多线程生产消费模式下,唤醒线程用notifyAll(),不要用notify(),可能会造成死锁
实现一个容器,使用生产者消费者进行容器消费,容器总容量为10.
第一种方式实现: 利用Codition 和 Lock 实现,Condition可以等待,唤醒一部分线程
1 |
|
第二种方式实现: 利用Codition 和 Lock 实现,Condition可以等待,唤醒一部分线程
1 | package com.yahui.fan.zookeeper.ThreadTest; |
ThrealLoacl 当前线程会复制一个变量副本,每个线程互不影响,synchronized 是一个线程完了之后另一个才可以执行,所以,ThreadLocal是空间换时间,synchronized是时间换空间
并发容器
map,set的使用
不同步,不加锁:
HashMap
TreeMap
HashSet
LinkedHashMap
同步,加锁:
并发不高:HashTable,Collections.synchronizeXXX.
并发高:ConcurrentHashMap
高并发,顺序:ConcurrentSkipListMap
ConcurrentHashMap 分为16个小的Map,分段加锁,性能高
Queue
1:高并发,内部加锁 – ConcurrentLinkedQueue 单向队列
offer() 增加,无界队列,如果内存够,一直可以加
poll() 取出第一个并删除
peek() 取出第一个,不删除
2:高并发,阻塞 LinkedBlockingQueue,ArrayBlockingQueue
put() 存,如果队列满了,阻塞等待
take() 取,如果队列空了,阻塞等待
代码实现:
1 | package com.yahui.fan.zookeeper.QueueTest; |
3: ArrayBlockingQueue 有界队列
// 队列里边只能装10个
BlockingQueue queue = new ArrayBlockingQueue(10);
queue.add(“i”); //容器满了会报异常
queue.offer(“i”); //容器满了不保异常,根据返回值判断是否添加成功
queue.offer(“i”;TimeUnit.SECONDS); 隔一段时间添加,加不进去,终止
queue.put(“i”); 容器满了,阻塞等待
4: DelayQueue 无界队列,定时队列,等待时间最长先出
BlockingQueue queue = new DelayQueue();
5: LinkedTransferQueue 如果消费者等待,那生产者queue任务到了不直接扔到队列,而是直接给了消费者queue
LinkdeTransferQueue queue = new LinkedTransferQueue();
queue.transfer(“a”);//生产的消息必须马上处理,不然阻塞
6: SynchronousQueue 同步队列
BlockingQueue queue = new SynchronousQueue();
queue.add(“”);//如果没有消费者,报错
queue.put(“”);//如果没有消费者,阻塞
线程池
Executor
Executors 工具类
ExecutorService -submit(Runnable/Callable) -execute(Runnable)
Runnable 没有返回值
Callable 有返回值,可以抛异常
ThreadPool
1 | package com.yahui.fan.zookeeper.ThreadPool; |
结果:
//queued tasks = 1 一个等待任务
java.util.concurrent.ThreadPoolExecutor@46ee7fe8[Running, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0]
false
true
java.util.concurrent.ThreadPoolExecutor@46ee7fe8[Shutting down, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0]
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
pool-1-thread-5
pool-1-thread-1
pool-1-thread-2
true
true
//线程池正常关闭,已执行任务列表
java.util.concurrent.ThreadPoolExecutor@46ee7fe8[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 6]
Future
1 | package com.yahui.fan.zookeeper.ThreadPool; |
结果:
19
100001
3
100001
线程池种类
//固定个数线程池
–1 ExecutorService executorService = Executors.newFixedThreadPool(5);
1 | public static ExecutorService newFixedThreadPool(int nThreads) { |
//缓存线程池,启动时池中没有线程,有任务过来,就创建,释放的就在线程池中缓存,下个任务来了继续用,如果一直没有任务用,默认60s自动关闭该线程
–2 ExecutorService executorService1 = Executors.newCachedThreadPool();
1 | public static ExecutorService newCachedThreadPool() { |
// 单例线程池 只有一个线程的线程池,可以保证扔到线程池里的任务是顺序执行
–3 ExecutorService executorService2 = Executors.newSingleThreadExecutor();
1 | public static ExecutorService newSingleThreadExecutor() { |
// 定时执行
–4 ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4);
1 | //方法 |
// 精灵线程,每个线程维护自己的队列,自己队列没有任务时,会执行别的线程队列里的任务
–5 ExecutorService executorService1 = Executors.newWorkStealingPool();
默认会根据当前机器的线程来启动相应数量的线程
//查看本机线程数
System.out.println(Runtime.getRuntime().availableProcessors());
// 当任务特别大时,分成多个小线程执行,最后在合并结果
–6 ForkJoinPool forkJoinPool = new ForkJoinPool();
—- 1,2,3,4种线程最终都返回 ThreadPoolExecutor
1 | return new ThreadPoolExecutor(0, Integer.MAX_VALUE, |
备注: java8 stream提供多线程计算 parallelStream()
1 | List<Integer> all = new ArrayList<>(); |