18年的暑假,在cloudwalk公司实习,准备着9月份的秋招,两个月的时间要学的东西有很多压力很大。
从苏州实习的那天开始就在火车上看这本书,高洪岩先生写的《Java多线程编程核心技术》,这本书很容易读懂,但是读了之后的感受就是太简单了,书中的内容也不算太多,5天的时间看了两遍。
大致可以分为四个部分:
第一部分是线程的创建,启动,暂停,恢复,停止,这部分是线程的基础。
第二部分是并发访问时变量(方法)的可见性和隔离性,这部分和第三部分是线程知识的核心。
第三部分多线程之间的通信,讲述了线程之间运行顺序的安排(等待/通知机制),这部分是建立在第二部分的基础上的。
第四部分:例如Timer计时器的使用,单例模式,java反射机制,序列化和反序列化,静态内部类,懒汉模式和饿汉模式,我将它成为java拓展。
下面这部分是写我在看第二遍时总结的内容:
第一章(基础):
1.创建线程
(1)extends Thread,重载run()(2)java 单继承机制,Thread 的构造方法 public class myRunable implements Runnable,Thread thread1=new Thread(myRunable),thread1.start()(3)java5 提供了Future接口来代表Callablej接口里的call()方法返回值,并且为Future 接口提供了一个实现类FutureTask,这个实现类既实现了Future又实现了Runnable接口,因此可以作为Thread的target.
future接口中定义了几个公共的方法来控制它关联的Callable任务。
get():返回call()方法的返回值
(1)创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例。
(2)使用FutureTask类来包装Callable对象,该FutureTask对象封装了Call()返回值
(3)使用FutureTask作为Thread的target创建并启动线程
(4)FutureTask对象的get()方法来获得子线程执行结束后的返回值
2.线程状态---停止线程
状态信息存储State枚举类中,Enum 实现了serialiable 可以执行性序列化
New:至今尚未被启动(start方法调用之前) 7RUNABLE:正在jvm中执行的线程处于这种状态
Blocked :受阻并等待某个监视器锁的线程所处的状态 Waiting:无限期的等另一线程来执行某一特定操作的线程
Timed Waiting :等待另一线程来执行取决于指定等待时间的操作的线程 TERNINATED:已退出的线程
(线程在销毁之前各状态之间可以循环转换的)
(1)Thread.sleep(long time):控制线程的执行进程
(2)Thread.interrupt():只是打了一个中止的标记,并不能终止一个正在进行的线程,还需要加一个判断才能完成线程的停止
Thread.java类中提供了两个方法:this.isInterrupted()和this.interrupted()
this.interrupted():当前线程是否停止,当前线程指这条语句所在的线程,且会清除停止状态
this.isInterrupted():线程是否停止
(3)通过报错异常的方式停止线程
try{
for(){
if(){
sout();
throws new InterruptedException();
}
}
}catch(InterruptedException e){
e.printStackTrace;
}
(4)thread.sleep(1000000)
thread.interrupte();
在停止状态下进入线程会进入catch,并清除线程的停止状态。
(5)interrupt()和return结合:和抛出异常的方法区别仅在于用return关键字来代替抛异常。
3.暂停线程,与恢复线程(suspend(),resume())
suspend与synchronized联合使用时暂停线程但是不释放对象锁,resume()方法才能使线程继续下去
println方法内也具备synchronized
yield():重新抢夺cpu 的使用权。
第二章:对象及变量的并发访问:Synchronized和volidate
多线程对变量的可见性和原子性(同步性)的操作
synchronized关键字的特征:前提变量是公用的(对象所持有的变量)不是方法的特有变量
(1)写在方法之前,表示线程持有该方法所属对象的锁Lock
(2)锁重入,一个对象中既有锁方法又有没加锁的方法,获得该对象锁后可以调用其他方法,其他线程可以调用该对象未加锁
的方法
(3)锁方法不具备继承性
(4)按顺序执行,同步的,阻塞的,影响效率
1.非线程安全是在多个线程对同一对象的实例变量进行并发访问时发生“脏读”解决脏读的方法:
(1)给并发的方法块加上synchronized关键字:这种方法让所有线程等待这个线程运行完毕后才能去争取线程的权限
(2)synchronized(this)代码块:提高了运行效率
(3)synchronized(非this对象x):与sychronized(this)不同级,注意x对象中可能含有synchronized和synchronized(this)锁
2.死锁:互相等待对方释放锁
解决办法就是转换锁对象
(1)用synchronized(非this对象x)代替 synchrinized方法块,将对象锁变为object 对象
3.volatile关键字:使变量在多个线程间可见
Thread.java
private volatile boolean isRunning=true;
while(isRunning==true)
Run.java
thread.setRunning(false)
原理:程序运行前,变量存在于公有栈堆和私有栈堆,线程为了保证效率,直接从线程的私有栈堆读取数据,而Run.java针对公有栈堆进行变量更新,volatile关键字的作用:当线程访问isRunning这个变量时,强制从公有栈堆中进行取值。
缺点:(1)只能修饰变量(2)不保证原子性(同步性)
相比,synchronized 可以使多个线程访问统一资源具有同步性,而且它还具有将线程工作内存中的私有变量与公共内存中的变量同步的功能。
第三章:线程之间的通信
线程之间的操作顺序,多线程操作公有变量时的可见性,和同步性
1.等待/通知机制:线程之间的操作顺序
两个线程协作完成工作:线程A先运行,while当满足某个条件时,线程B运行
这种while语句轮询的方法:时间间隔小,浪费cpu资源,为了减少cpu资源的浪费,所以有了“wait/notify”机制
wait()是Object类的方法,在调用wait()之前,线程必须先获得该对象的对象级别锁,即只有在同步方法和同步块之中才能调用wait()方法,之后会释放锁,没有持有锁时会抛出IllegalMonitorStateException
notify()调用的条件与wait()相同,但是notify()调用后并不会立即释放锁,所有等待锁也是公平竞争。
notifyAll()唤醒所有wait()的锁
在p_r(多个消费者多个生产者)由此可以引发问题:notify()并不能指定唤醒哪种锁,所以最终可能唤醒了同类锁,导致死锁的状态
2.join:是使所属的线程对象x正常执行run()的任务,而当前的线程z进行无限期的阻塞,等待线程x销毁后再继续执行线程z之后的代码。
join 在内部使用wait()进行等待,而synchronized关键字使用的是“对象监视器”原理做同步
join()遇到interrupt()会报错,join(long)线程z只等待对象x所属的线程运行long
3.ThreadLocal:解决每个线程绑定自己的值,变量在每个线程之间是隔离的,不同线程拥有自己的值
public class tools(){
private ThreadLocal t1=new ThreadLocal();
}
threadA.java
tools.t1.get();tools.t1.set(i+1)
threadB.java
tools.t1.get();tools.t1.set(i+1)
虽然每个线程都向t1 set数据值,但是每个线程还是能取出自己的数据
InheritableThreadLocal的使用:在子线程中取得父线程继承下来的值。
第四章:Lock的使用:针对第二三章多线程通信和同步的技术更迭
1.线程同步技术:使用synchronized可以实现线程之间的互斥,jdk1.5中增加了ReentrantLock类也能达到同样的效果,并具备以下的拓展功能:嗅探锁定,多路分支通知等功能
使用方法:(1)创建ReentrantLock对象,Lock lock=new ReentrantLock();
(2)lock.lock()获取对象锁
(3)lock.unlock()释放锁
2.线程间的通信技术:ReentrantLock借助Condition对象
Lock 对象里面可以创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更灵活。
使用方法
(1)Lock对象中创建多个Condition(对象监视器)实例
Myservice.java
private Lock lock =new ReentrantLock()
private Condition condition=lock.newCondition()
//此处可以添加多个Condition的实例 conditionA 和conditionB,可以指定condition进行进行signal()
pubic void await(){
lock.lock()
condition.await() //必须在此之前调用lock.lock()获得对象监视器
lock.unlock() //释放锁
}
(2)线程对象
Thread.java
service.await()
(3)启动线程
threadA.start()
condition.signal()相当于Object类中的notify()
3.公平锁和非公平锁
公平锁:线程获取锁的顺序是按照线程加锁的顺序来分配的,即FIFP,先进先出
Lock lock=new ReentrantLock()
到此,知识总结也算是结束了,掌握了这些也算是多线程入门了,更多知识还要我们努力寻找。为了秋招,我会更努力的看更多的书,在这里和大家分享,加油。