一 多线程的创建
1、thread/runnable
thread:继承Thread类
runnable:实现Runnable接口
2、两种启动线程方式的区别
共同点:必须通过Thread来产生线程,然后调用start方法来开启线程
不同点:接口比继承更灵活;
如果需要开启多个线程,runnable实现整个资源的共享,继承thread的话,需要new 多个线程。
大多通过实现Runnable接口来实现多线程。适合多个相同代码处理统一资源的复用
3、start方法和run方法的区别
strat/run
start开启一个线程,该线程为就绪状态,
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行
二 线程间通信
1、synchronized关键字【关键】
1)synchronized对象锁【锁住的是当前对象】
2)synchronized来实现线程间通信【通过不同的线程来共享同一个对象来进行锁的管理】
3)synchronized/volatile 【二者区别】
当一个变量没有修饰时,在单个线程中获取到的值实际为其在线程中拷贝的值,而非主内存中的值。
volatile 不允许线程将一个变量从主内存中拷贝到自己的空间
synchronized 锁住一个对象后,其他线程无法进入。同步主内存中的数据,之后执行自己的代码,再更新主内存中的数据,其他线程也更新该变量的值。
synchronized:可以使用在类,方法,代码块,变量上,占用资源略多
volatile:只能使用在变量上,更高效
4)synchronized/lock
synchronized:用法上
lock:需要指定起始位置【自己调用lock.lock();//得到锁 lock.unlock();// 释放锁】,一般需要在finnally中UnLock.
效率:synchronized托管给JVM虚拟机执行的,Lock我们java代码自己写的控制代码,
synchronized相对低效
synchronized采用悲观锁,Lock是CPU乐观锁
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
2、sleep/wait
锁:sleep是Thread native方法,它在设定时间内阻塞线程的执行,不会改变线程的锁的持有情况。而wait方法需要锁来控制的。
sleep()
sleep() 方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。
因为sleep() 是static静态的方法,他不能改变对象的机锁,【当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁没有被释放,其他线程依然无法访问这个对象。】
wait()
wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的机锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程
3、wait/notify机制
锁:wait定义在Object中的,需要在同步代码块中调用,调用完成之后会释放锁,并进入锁对象的等待队列。
需要其他线程调用notify方法释放锁之后,它才能重新竞争这个锁。
三 线程池
1、好处
1)降低资源消耗【通过重复利用已经创建的线程,降低重复创建或者销毁线程所带来的消耗】
2)提高响应速度【不需要等到线程创建,即可立即执行】
3)提高线程的可管理性【可以进行统一的分配】
2、ThreadPoolExecutor
new ThreadPoolExecutor(corePoolSize,maximumPoolSize,
keepAliveTime,milliseconds,unnableTaskQueue,handler);
1)corePoolSize 线程池的基本大小
2)maximumPoolSize 最大所能容纳的线程大小【】
3)keepAliveTime 线程保持存活的时间
4)milliseconds 时间单位
5)runnableTaskQueue 线程阻塞队列
6)ThreadFactory 创建线程的工厂类
7)handler 饱和策略
创建线程池--->提交任务
线程池的隐患之处,我们没有办法判断该任务是否执行成功
3、线程池的工作流程
1)首先线程池判断基本线程池是否已满
2)其次线程池判断工作队列是否已满
3)最后线程池判断整个线程池是否已满
网友评论