资源预览内容
第1页 / 共10页
第2页 / 共10页
第3页 / 共10页
第4页 / 共10页
第5页 / 共10页
第6页 / 共10页
第7页 / 共10页
第8页 / 共10页
第9页 / 共10页
第10页 / 共10页
亲,该文档总共10页全部预览完了,如果喜欢就下载吧!
资源描述
1、线程的状态1.1创建 线程 的两种方式,接口和线程类。利用接口的好处:更好的体现面向对象的思想,可以避免由于Java的单继承特性而带来的局限;增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的;(同步问题)适合多个相同程序代码的线程区处理同一资源的情况。(关注微信订阅号:javaedu)1.2线程就绪等待调度运行start()方法。1.3线程的中断这里需要注意的是,如果只是单纯的调用interrupt()方法,线程并没有实际被中断,会继续往下执行。1.4、线程挂起和恢复(挂起还拥有对象锁,死锁)线程的挂起和恢复实现的正确方法是:通过设置标志位,让线程在安全的位置挂起1.5 利用多线程模拟同步运行用jion方法,mThread.jion()表示该线程运行完毕后,在运行调用它的线程。1.6 sleep 休眠当线程执行Thread.sleep()时,它一直阻塞到指定的毫秒时间之后,或者阻塞被另一个线程打断;1.7stop线程停止stop方法突然终止线程(持有这些锁必定有某种合适的理由也许是阻止其他线程访问尚未处于一致性状态的数据,突然释放锁可能使某些对象中的数据处于不一致状态)1.8线程可以阻塞于四种状态:(参考资料:t.cn/RA5iKhq)当线程执行Thread.sleep()时,它一直阻塞到指定的毫秒时间之后,或者阻塞被另一个线程打断;当线程碰到一条wait()语句时,它会一直阻塞到接到通知(notify()、被中断或经过了指定毫秒时间为止(若制定了超时值的话)线程阻塞与不同I/O的方式有多种。常见的一种方式是InputStream的read()方法,该方法一直阻塞到从流中读取一个字节的数据为止,它可以无限阻塞,因此不能指定超时时间线程也可以阻塞等待获取某个对象锁的排他性访问权限(即等待获得synchronized语句必须的锁时阻塞)。2、线程的种类守护线程与线程阻塞的四种情况Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。守护线程是否已经完成了预期的服务任务。一旦所有的用户线程退出了,虚拟机也就退出运行了。因此,不要在守护线程中执行业务逻辑操作(比如对数据的读写等)。、setDaemon(true)必须在调用线程的start()方法之前设置,否则会跑出IllegalThreadStateException异常。在守护线程中产生的新线程也是守护线程。不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑。3、线程所操作的数据同步问题:4、可重入内置锁每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁。线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁。获得内置锁的唯一途径就是进入由这个锁保护的同步代码块或方法。某一个持有同步对象锁的线程可以多次进入这个同步代码块或方法。即同步对象锁可以重入!同一个线程在调用本类中其他synchronized方法/块或父类中的synchronized方法/块时,都不会阻碍该线程地执行,因为互斥锁时可重入的。6、Java内存模型在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。(本地内存+共享主存)Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。而volatile关键字就是提示JVM:对于这个成员变量,不能保存它的私有拷贝,而应直接与共享成员变量交互。volatile型变量的特殊规则:1、保证此变量对所有线程的可见性。需要注意,volatile变量的写操作除了对它本身的读操作可见外,volatile写操作之前的所有共享变量均对volatile读操作之后的操作可见2、禁止指令重排序优化final域能确保初始化过程的安全性,从而可以不受限制地访问不可变对象,并在共享这些对象时无须同步因此在编码时,不需要将long和double变量专门声明为volatile。主内存与工作内存Java内存模型规定所有的变量都存储在主内存中,而每条线程还有自己的工作内存,线程的工作内存中保存了该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。根据Java虚拟机规范的规定,volatile变量依然有共享内存的拷贝,但是由于它特殊的操作顺序性规定从工作内存中读写数据前,必须先将主内存中的数据同步到工作内存中,所有看起来如同直接在主内存中读写访问一般,因此这里的描述对于volatile也不例外不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存。如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。对一个变量执行unlock操作之前,必须先把此变量同步回主内存(7、轻量级同步volatile是一种稍弱的同步机制,在访问volatile变量时不会执行加锁操作,也就不会执行线程阻塞,因此volatilei变量是一种比synchronized关键字更轻量级的同步机制。使用建议:在两个或者更多的线程需要访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,没必要使用volatile。由于使用volatile屏蔽掉了JVM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。假如有两个线程分别读写volatile变量时,线程A写入了某volatile变量,线程B在读取该volatile变量时,便能看到线程A对该volatile变量的写入操作,关键在这里,它不仅会看到对该volatile变量的写入操作,A线程在写volatile变量之前所有可见的共享变量,在B线程读同一个volatile变量后,都将立即变得对B线程可见。happenbefore规则介绍其意思就是说,在发生操作B之前,操作A产生的影响都能被操作B观察到,“影响”包括修改了内存中共享变量的值、发送了消息、调用了方法等,它与时间上的先后发生基本没有太大关系。线程启动规则:Thread对象的start()方法happenbefore此线程的每一个动作。8、同步锁说明当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。任何一个对象系统都会为其创建一个互斥锁,这个锁是为了分配给线程的,防止打断原子操作。每个对象的锁只能分配给一个线程,因此叫做互斥锁。如果同一个方法内同时有两个或更多线程,则每个线程有自己的局部变量拷贝。类的每个实例都有自己的对象级别锁访问同一个类的不同实例对象中的同步代码块,不存在阻塞等待获取对象锁的问题,因为它们获取的是各自实例的对象级别锁,相互之间没有影响。持有一个对象级别锁不会阻止该线程被交换出来,也不会阻塞其他线程访问同一示例对象中的非synchronized代码。持有对象级别锁的线程会让其他线程阻塞在所有的synchronized代码外。使用synchronized(obj)同步语句块,可以获取指定对象上的对象级别锁。类级别锁被特定类的所有示例共享,它用于控制对static成员变量以及static方法的并发访问。具体用法与对象级别锁相似。10、多线程环境中安全使用集合API最初设计的Vector和Hashtable是多线程安全的。在Collections类 中有多个静态方法,它们可以获取通过同步方法封装非同步集合而得到的集合:public static List synchronizedList(list l)List list = Collection.synchronizedList(new ArrayList();注意,ArrayList实例马上封装起来,不存在对未同步化ArrayList的直接引用(即直接封装匿名实例)。这是一种最安全的途径。如果另一个线程要直接引用ArrayList实例,它可以执行非同步修改。从内存可见性的角度看,写入volatile变量相当于退出同步代码块,而读取volatile变量相当于进入同步代码块。、volatile变量是一种稍弱的同步机制在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比synchronized关键字更轻量级的同步机制。原因是声明为volatile的简单变量如果当前值与该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子操作:“count+”、“count = count+1”。当且仅当满足以下所有条件时,才应该使用volatile变量:1、对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。2、该变量没有包含在具有其他变量的不变式中。三、死锁问题遵循以下原则有助于规避死锁:1、只在必要的最短时间内持有锁,考虑使用同步语句块代替整个同步方法;2、尽量编写不在同一时刻需要持有多个锁的代码,如果不可避免,则确保线程持有第二个锁的时间尽量短暂;3、创建和使用一个大锁来代替若干小锁,并把这个锁用于互斥,而不是用作单个对象的对象级别锁;四、线程通信在调用wait()之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。进入wait()方法后,当前线程释放锁。notify() 该方法也要在同步方法或同步块中调用,即在调用前,线程也必须要获得该对象的对象级别锁,的如果调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException。notify后,当前线程不会马上释放该对象锁,wait所在的线程并不能马上获取该对象锁,要等到程序退出synchronized代码块后,当前线程才会释放锁,wait所在的线程也才可以获取该对象锁),但不惊动其他同样在等待被该对象notify的线程们。当第一个获得了该对象锁的wait线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,会继续阻塞在wait状态,直到这个对象发出一个notify或notifyAll。这里需要注意:它们等待的是被notify或notifyAll,而不是锁。这与下面的notifyAll()方法执行后的情况不同。notifyAll使所有原来在该对象上wait的线程统统退出wait的状态(即全部被唤醒,不再等待notify或notifyAll,但由于此时还没有获取到该对象锁,因此还不能继续往下执行),变成等待获取该对象上的锁,一旦该对象锁被释放(notifyAll线程
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号