[TOC]
问题:
并发编程不可不知道的基础概念
天使就是多线程的java程序
线程的一生
线程间的共享
线程间的协作
线程隔离的threadLocal
java里的显示锁
多线程
并发编程不可不知道的基础概念
线程:cpu的核心数和线程数的关系
核心数和线程数: 是1:1的关系,如果cpu是4核的话,就可以同时4个线程工作
如果是inter的超线程技术:一个核对应了2个逻辑cpu,所以有了4核8线程
cpu时间片轮转机制 RR调度
Round-Robin,轮询调度,通信中信道调度的一种策略,该调度策略使用户轮流使用共享资源,不会考虑瞬时信道条件。
进程和线程
进程和线程:
进程:操作系统在运行的时候,资源分配的最小单位;分配磁盘io,cpu等;
进程和cpu无关
线程:线程必须依赖一个进程,启动一个进程,至少有一个线程。
linux和windows的进程数量有所不同,但是可以进行认为的修改。
并发和并行
并行:同时进行,四个轮子的车。
并发:关键字:单位时间内;在单位时间内运行的线程数量。
java是不怎么关注虚拟机的内核,java无法调度一个线程在指定cpu核上运行。
并发会来带一下问题:
1.线程安全问题
2.死锁问题
3.线程数量,会消耗内存,有单独的栈空间,每个栈空间需要1m的空间的大小
一个线程,在运行的时候,会消耗,运行的数据也要保持起来,另一个线程,也要去读,存好的数据,去取,取的时候也需要时间
通过上下文切换来进行切换线程的使用。一般消耗20000个cpu时间周期,时间周期:把于1+1的时间看作成一个时间周期。
finalize() 关于对象,释放
用finalize线程来进行,但是这个方法不一定执行finalize线程是个守护线程,守护线程跟主线程是同生共死的,当主线程销毁了,finalize线程也进行了销毁,所以finalize()方法就不会被调用了
问题:gc线程是什么时候启动的?
答:gc线程,要有资源回收的时候才启动答:gc是个守护线程,和主线程同生共死的
Thread
开启一个线程
总共3种启动方式
分别是:thread
然后两种任务的形式:runnable,callable (callable这种方式不经常使用,在android asyncTask中使用了这个种方式,然后进行的)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17>Runnable runnable = () -> {
System.out.println("我是runnable的方式启动的");
};
Thread thread = new Thread() {
public void run() {
super.run();
}
};
>static Callable<String> callable = new Callable<String>() {
public String call() throws Exception {
System.out.println("我是callable的");
Thread.sleep(2000);
return "aaaa";
}
>};停止一个线程
抛弃的方法
Suspend 会拿着锁去挂起,容易造成死锁
stop, 会直接停止线程,会有些文件句柄等一些都没有进行释放
jdk 1.5后建议使用
Interrupted -> 是一个静态方法;判断线程是否被中断,这个会把状态true改为false
isInterrupted -> thread的成员方法;是否中断了, 而这个中断状态是true,不会改成false
静态方法 方法那个在使用过程中,通过这个状态来进行判断处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14//1.静态方法
//这个情况下,就可能有不一样的效果了
while (Thread.interrupted()) {
//执行的内容
}
//2.成员方法,
while (Thread.interrupted()) {
//执行的内容
}
// runnable,或者callable的情况下获取当前线程进行 interrupt
while (Thread.currentThread().isInterrupted()) {
//执行的内容
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27//如果是在注释的这个打印,最后结果会是true,
while (!Thread.currentThread().isInterrupted()) {
System.out.println("TestRunnable enter isInterrupted: " + Thread.currentThread().isInterrupted());
}
//这样打印,最后一行输出会是false
public class TestRunnable implements Runnable {
public void run() {
// while (!Thread.currentThread().isInterrupted()) {
// System.out.println("TestRunnable enter isInterrupted: " + Thread.currentThread().isInterrupted());
// }
while (!Thread.interrupted()) {
System.out.println("TestRunnable enter isInterrupted: " + Thread.currentThread().isInterrupted());
}
System.out.println("TestRunnable end isInterrupted: " + Thread.currentThread().isInterrupted());
}
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new TestRunnable();
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}所以,Thread.interrupted() 是会把状态重新修改一次
资料说明:
interrupted()是静态方法:内部实现是调用的当前线程的isInterrupted(),并且会重置当前线程的中断状态
isInterrupted()是实例方法,是调用该方法的对象所表示的那个线程的isInterrupted(),不会重置当前线程的中断状态
其实看源码就知道了:
Tests if some Thread has been interrupted. The interrupted state
is reset or not based on the value of ClearInterrupted that is
passed
这句话的翻译
测试某个线程是否被中断。中断状态是重置还是不重置取决于clearinterrupt的值通过。1
2
3
4
5
6
7
8
9
10
11
12>public static boolean interrupted() {
return currentThread().isInterrupted(true);
>}
>public boolean isInterrupted() {
return isInterrupted(false);
>}
>/**
* Tests if some Thread has been interrupted. The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
*/
>private native boolean isInterrupted(boolean ClearInterrupted);
线程的共享和协作
线程之间的共享:数据是共享的;线程共享进程的所拥有的全部资源。
线程基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器,栈),但是它可与同属一个进程的其他线程共享进程的所拥有的全部资源
有共享就会出现,线程安全问题,从而出现了锁的这个概念
synchronized 内置锁 : synchronized锁的都是对象
对象锁,在静态方法上面的锁,是class对象锁
在普通方法上面的锁,是this 这个对象的锁Lock 显示锁
可重入锁解释
1
2
3
4
5
6
7
8
9
10
11/**
* 什么叫可重入锁:
* 自己把自己锁上了
* synchronized 支持,多次拿自己的,jdk设计的时候,考虑了这一点,所以也是可重入锁
* 递归调用的时候,防止自己把自己锁死
*/
private int count;
public synchronized void incr() {
count ++;
incr();
}生产者与消费者标准范式
1
2
3
4
5
6
7
8
9
10
11
12
13
14//消费者
sync(对象) {
while(条件不满足) {
对象.wait()
}
执行业务代码
}
//生产者
sync(对象) {
执行业务代码
修改条件
对象.notify/notifyAll
}非公平锁和公平锁
疑问:threadpoolexecutor 是怎么保留线程数量的,线程池的原理
通过死循环
扩展:final类型的应该不能反射修改吧???
回答是分两种情况的。
当final修饰的成员变量在定义的时候就初始化了值,那么java反射机制就已经不能动态修改它的值了。
当final修饰的成员变量在定义的时候并没有初始化值的话,那么就还能通过java反射机制来动态修改它的值。
1 | public class People { |
把people改成这样就可以进行修改了
1 | public final String name ; |
2019-04-11整理
复习了synchronized 关键字的复习
ThreadLocal线程隔离
synchronized同步中使用 wait来等待, notify notifyAll来进行唤醒读写锁,来进行单一写,多读的情况下,可以提高效率
可重入锁概念:自己可以调取自己不会因为锁来卡死自己
正式进入话题
1. Condition lock的这个锁中有个 newCondition的这个方法,返回一个condition对象
Condition 里面有几个方法
主要有: await() 相当于wait()
signal()/signalAll() 相当于notify、notifyAll,但是不同的是,当signal()之后,只会唤醒对应的Condition对象,所以当一对一唤醒的时候,比notify要好用,notify可能会唤醒不该唤醒的线程。
// todo 明天补上代码
2.线程池
为什么要使用线程池
- 线程池可以复用线程,降低反复创建线程的消耗
- 提高响应速度 (来回创建肯定消耗时间和资源)
- 提高线程的可管理性
runtime 可以获取到 jdk运行时的一些信息
1
Runtime.getRuntime().availableProcessors(); //获取 cpu 逻辑核心数
在看线程池的源码过程中,犯了一个错误,看到 runnable.run()误认为是普通方法,并没有开启线程,实际上是在封装好的 Thread内里面的run方法调用的,所以通过普通方法调用,这种方式是合理的
还是没找到循环在哪里,还没搞懂,有问题????todo
线程池 worker 线程 中 run 方法 runWorker(this) 里面进行了调取 getTask()
线程池中通过线程自己里面在 getTask的时候 workQueue.take(); 进行堵塞,然后让线程存活了起来线程池的使用
BlockingQueue对应的几个常用的方法
add() 增加 会抛异常
remove() 删除 会抛异常offer() 插入成功返回true 失败返回 false
poll() 移除队列的元素
put() 数据满,插入堵塞
tack() 为空时,取出堵塞线程池的一些参数含义
1
2
3
4
5
6
7
8public ThreadPoolExecutor(int corePoolSize, //线程池的核心线程数
int maximumPoolSize, //线程池最大线程数 10 就有5个非核心线程数
long keepAliveTime, //线程池当达到最大线程数的时候,非核心线程数的线程存活的时间
TimeUnit unit, //时间单位
BlockingQueue<Runnable> workQueue, //排队的任务队列
ThreadFactory threadFactory, //线程的命名??
RejectedExecutionHandler handler //队列+最大线程数已经极限了,然后做出的处理
)jdk的4种拒绝策略(TODO)
启动线程池的两种方式
execute()
submit() —AbstractExecutorService实现的
线程池核心数量的确定
>1
2
3
4
5
6
>* 任务的特性相关:
>* cpu密集型: 当前计算任务需要cpu 不超过机器上的同时运行的线程个数 cpu逻辑核心数
>* IO密集型: 读写操作时,网络的时候 一般来讲配置多点线程数 2*cpu逻辑核心数
>* 混合型 --> 拆分 为 cpu密集型和io密集型
>* io 和 cpu消耗的差不多的时候 拆分
>* io 10s cpu 10ms 相当于 cpu密集型 就不需要拆分了
所有的ui控件都是线程不安全的
AsyncTask的原理 //todo
Volatile 可变的 //todo
都会到主内存去取,但是写回到主内存的时候并不安全
适用于:当一个写,多个读的时候,非常适用悲观锁 synchronized和lock都是悲观锁
乐观锁 cas写回操作,知道相等为止
典型的悲观锁
AtomicBoolean , AtomicInteger1
2
3
4
5
6
7
8
9在unsafe.class里面有
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
//这些细节决定了这些
1
StickyRecyclerHeadersAdapter
//将一个item回到顶部
1
((LinearLayoutManager) mRecyclerView.getLayoutManager()).scrollToPositionWithOffset(posi, 0);