收藏 分享(赏)

JAVA线程.ppt

上传人:gsy285395 文档编号:4529201 上传时间:2019-01-02 格式:PPT 页数:39 大小:2.30MB
下载 相关 举报
JAVA线程.ppt_第1页
第1页 / 共39页
JAVA线程.ppt_第2页
第2页 / 共39页
JAVA线程.ppt_第3页
第3页 / 共39页
JAVA线程.ppt_第4页
第4页 / 共39页
JAVA线程.ppt_第5页
第5页 / 共39页
点击查看更多>>
资源描述

1、JAVA线程基础,核心平台 李均柠 2010年12月,目录,线程简介 本地线程变量 线程安全与同步 线程协作 java.util.concurrent介绍,2,3,4,1,5,目录,线程简介,1,线程简介基本概念,什么是线程线程的执行动态的运行过程线程对象装载线程代码,提供线程操作,反映线程状态创建线程对象继承Thread类实现Runnable接口启动线程Thread对象的start方法对于操作系统而言,JVM是一个进程,JVM环境中所有的代码以线程的方式来运行,所有线程执行结束时,JVM进程也结束 一个线程只能被启动一次(调用start方法) 对于CPU来说,每个时刻只能执行一个线程;在一段

2、代码中顺序调用多个线程的start方法启动,并不意味着它们按顺序执行;虽然代码不能直接控制线程的CPU调度,但可以通过设置优先级来影响调度 启动线程会造成较大的开销,尽量利用线程池,线程简介基本方法,获取线程对象 Thread.currentThread()得到当前的线程对象 休眠 Thread.sleep使当前线程进入休眠状态 线程ID getId()自动生成的长整型唯一号 线程名称 getName()用户可通过setName()设置名称,如果没有指定,JVM会自己取名,主线程的名字总是main 线程优先级 getPriority()0-10的整数,默认为5,通过setPriority()方

3、法设置在相同优先级的情况下,可以调用yield()短时间内让出执行权 线程状态 getState() isAlive()Thread.State 的6种状态 异常处理 getUncaughtExceptionHandler()实现一个异常处理者,通过setUncaughtExceptionHandler来设置给线程对象 线程组 getThreadGroup()每个线程都属于某个线程组;创建线程对象时可以指定所属线程组,如果没有指定则与创建者属于同一个组ThreadGroup用来管理一组线程,ThreadGroup中的某些方法,可以对所有的线程产生作,例如interrupt()方法可以中断线程组

4、中所有的线程,线程简介问题1,父线程执行结束后,其创建的子线程是否也强制结束,JUNIT实例: public class JunitDemo extends TestCasepublic void testThread()Thread t = new Thread(new Runnable()public void run() try Thread.currentThread().sleep(1000); catch (InterruptedException e) System.out.println(“thread running“););t.start();System.out.prin

5、tln(“junit thread demo end“); 以上代码执行能否输出 thread running?,线程启动后,不受父线程的约束,父线程结束后子线程依然运行 Junit使用了System.exit(0)的方法来退出JVM进程,线程简介结束线程,怎样正确的结束线程,stop suspend resume ?,让线程正常执行完,定时轮询检查业务条件while(isXXX)doXXX();Thread.sleep(xxx);,线程简介中断线程,Interrupt做了什么:1.设置线程中断标记2.让阻塞线程抛出InterruptedException,Interrupt做不了什么:1.

6、socket I/O阻塞的线程无法中断2.synchronized中的线程无法中断 怎么办:1.调用Socket的close方法2.不使用synchronized使用java.util.concurrent.locks.Lock,利用interrupt来中断while(!this.isInterrupted()doXXX();try Thread.sleep(xxx); catch (InterruptedException e) ,线程简介Daemon线程,线程分类 用户线程包括main线程在内,通常情况下我们创建的线程都是用户线程守护线程在start之前可以通过调用setDaemon(tr

7、ue)把线程设置为Daemon模式在Daemon线程中新建的线程默认也是Daemon线程 当JVM中所有的用户线程执行结束时,Daemon线程将自动退出我们可以把一些需要无休止执行的后台操作放到守护线程中,例如打印JVM运行日志,Java垃圾回收线程就是一个典型的守护线程尽量不要在守护线程中进行文件读写,网络,数据库等连接操作,目录,本地线程变量,2,本地线程变量ActionContext,ActionContext.getContext().getParameters(); ActionContext.getContext().getSession(); ServletActionConte

8、xt.getRequest(); ServletActionContext.getRequest().getSession();在Action中执行以上代码的结果是? 在main方法中执行以上代码的结果是?为什么?,本地线程变量ThreadLocal,需求场景:1.在一个线程中共享状态2.在同一个线程中频繁访问某个对象ThreadLocal类由四个方法组成initialValue(),get(),set(T),remove()其中initialValue()是一个protected的方法,用来让子类重写,该方法返回当前线程在该线程局部变量的初始值,在一个线程第1次调用get()或者set(Ob

9、ject)时才执行,并且仅执行1次。使用介绍见代码示例,本地线程变量ThreadLocal原理,方式1,方式2,本地线程变量注意事项,不要为了贪图方便而滥用ThreadLocal使用ThreadLocal的时候最好把它声明为static在线程退出前一定要重置你设置的对象最好只使用一个ThreadLocal对象,或者写一个ThreadLocal子类封装重置流程,目录,线程安全与同步,3,线程安全与同步重复订单问题,public class CreateOrderAction extends ActionSupportprivate OrderService orderService;public

10、 String execute()synchronized(this)orderService.createOrder();return SUCCESS; 以上代码能够防止同一订单重复创建吗?,线程安全与同步同步的原理,每一个对象实例都有一个monitor(监视者,或者叫对象锁) 进入synchronized代码块时获得对象锁 离开synchronized代码块时释放对象锁 一个线程获取了某个对象锁后其他线程不能获得,只能阻塞等待 判断是否同步的关键在于看synchronized代码究竟获取了哪个对象锁,线程安全与同步何谓线程安全,Servlet,JSP线程安全吗 Action线程安全吗 Ha

11、shTable线程安全吗,线程安全主要体现在以下两方面:1.多个thread对同一个对象实例的访问(read和modify)不会相互干扰,它主要体现在关键字synchronized。如ArrayList和Vector,HashMap和Hashtable(后者每个方法前都有synchronized关键字)2.每个线程都有自己的字段,而不会在多个线程之间共享。它主要体现在java.lang.ThreadLocal类,而没有Java关键字支持,如像static、transient那样,线程安全与同步不安全因素,i为共享变量public void counting()i = i+1;,JAVA线程代码

12、的不安全因素: 两个线程同时执行以上代码,假设此时i值为1 线程A加载变量i的值为1 线程A计算i+1的值为2 线程B加载变量i的值为1 线程B计算i+1的值为2 线程A把i赋值为2 线程B把i赋值为2,即使线程A在线程B进入counting()前先执行了i=i+1就能保证B线程看到正确结果吗?是否还有其他问题?,线程安全与同步JAVA内存模型,从主存复制变量到当前工作内存 (read and load) 执行代码,改变共享变量值 (use and assign) 用工作内存数据刷新主存相关内容 (store and write),线程安全与同步原子神话破灭,i为共享变量public void

13、 counting()i = i+1;,从主存赋值到工作内存,从工作内存刷新到主存的过程不能保证有序执行 1.线程A已经执行完i=i+1语句,但是线程B并没有从主内存中read&load 2.线程A已经执行完i=i+1语句,但是还没有向主内存中刷新还存在原子操作吗?,线程安全与同步DCL的杯具,if(obj=null)obj = new XXXObj() 经过Symantec JIT编译器编译过以后,大致会做以下三件事情:1.给XXXObj的实例分配内存。 2.初始化XXXObj的构造器 3.将XXXObj对象指向分配的内存空间(注意到这步XXXObj就非null了)。根据JVM规范,2,3两

14、个步骤的顺序允许互换! 单线程没有问题,因为需要执行完以上三个指令才能执行后续代码多线程环境下,线程A在对象XXXObj没有构造完成的情况下就把地址赋给了obj,此时如果JVM进行了一次线程切换,让线程B获得CPU,并且B在代码中使用到了obj里面未被初始化的属性,杯具就发生了!,线程安全与同步谁是救世主,synchronized保证了JMM的可视性和有序性 1.进入synchronized代码段后所有的变量都先从主存中加载 2.离开synchronized代码段前所有的线程变量都刷出到主存volatile 局部的synchronized 1.保证了赋值操作的原子性(线程直接操作主存)Java

15、存储模型的happens-before规则: (1)同一个线程中的每个Action都happens-before于出现在其后的任何一个Action。 (2)对一个监视器的解锁happens-before于每一个后续对同一个监视器的加锁。 (3)对volatile字段的写入操作happens-before于每一个后续的同一个字段的读操作。 (4)Thread.start()的调用会happens-before于启动线程里面的动作。 (5)Thread中的所有动作都happens-before于其他线程检查到此线程结束或者Thread.join()中返回或者Thread.isAlive()=fal

16、se。 (6)一个线程A调用另一个另一个线程B的interrupt()都happens-before于线程A发现B被A中断(B抛出异常或者A检测到B的isInterrupted()或者interrupted())。 (7)一个对象构造函数的结束happens-before与该对象的finalizer的开始 (8)如果A动作happens-before于B动作,而B动作happens-before与C动作,那么A动作happens-before于C动作。,目录,线程协作,4,线程协作从生成报表说起,需求描述:同一时刻只允许最多5个线程在生成报表,新请求需等待前请求执行完成,方法:利用sleepI

17、nteger count = 0;/定义共享计数器变量public void doService()count+;while(count5)try Thread.sleep(1000); catch (InterruptedException e) generateReport();/调用生成报表的方法count-;,问题:宝贵的生命不应在无价值的睡眠中虚度!,线程协作还有什么方法,方法2:利用同步Integer count = 0;public void doService()if(incrementAndCheck(4)generateReport();elsesynchronized(t

18、his)generateReport();decrement();/计数器减1,放到finally里面执行比较好public synchronized void decrement()count-;public synchronized boolean incrementAndCheck(int i)count+;return counti;,问题:虽然排队是美德,其他窗口没人了也要学会换地方,线程协作等待?通知?,排队等的时候有谁能通知一下吗?,wait,notify,notifyAll,谁的方法?怎么用?,wait,notify,notifyAll是Object的方法 必须在synchro

19、nized代码段中调用,并且只能被对象monitor的持有者调用 wait释放已经获得的monitor,让当前线程进入休眠状态,并等待monitor的唤醒通知 notify随机通知某一个正在wait的线程,让其进入synchronized同步阻塞状态 notifyAll通知所有正在wait的线程,让其进入synchronized同步阻塞状态,sleep睡到自然醒 wait 等着你回来,线程协作简单的资源控制,public void doService()while(true)if(checkAndIncrement(5)generateReport();decrement();synchron

20、ized(this)this.notify();break;synchronized(this)try this.wait(); catch (InterruptedException e) e.printStackTrace();public boolean checkAndIncrement(int i)synchronized(count)if(counti)count+;return true;return false;,目录,java.util.concurrent介绍,5,JDK线程工具线程池,创建一个可重用固定线程数的线程池 ExecutorService pool = Exec

21、utors.newFixedThreadPool(5);Thread t = new MyThread(); pool.execute(t); 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 ScheduledExecutorService pool = Executors.newScheduledThreadPool(2); Thread t = new MyThread(); pool.schedule(t, 1000, TimeUnit.MILLISECONDS); 创建一个自定义线程池ThreadPoolExecutor(int corePoolSize, int max

22、imumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue workQueue,RejectedExecutionHandler handler)corePoolSize: 线程池维护线程的最少数量maximumPoolSize:线程池维护线程的最大数量keepAliveTime: 线程池维护线程所允许的空闲时间unit: 线程池维护线程所允许的空闲时间的单位workQueue: 线程池所使用的缓冲队列handler: 线程池对拒绝任务的处理策略,JDK线程工具线程池,当一个任务通过execute(Runnable)方法欲添加到线程

23、池时:如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务

24、。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。,JDK线程工具线程池,workQuue的三种策略无队列,采用SynchronousQueue,此时认为队列大小为0,所有任务直接提交,直至超过maximumPoolSizes 无限队列,采用LinkedBlockingQueue,此时队列大小无限,所有超过corePoolSize的任务都进入队

25、列,maximumPoolSizes参数失效 有界队列,采用ArrayBlockingQueue,可以设定队列大小Handler的四个选择 ThreadPoolExecutor.AbortPolicy()抛出java.util.concurrent.RejectedExecutionException异常 ThreadPoolExecutor.CallerRunsPolicy()重试添加当前的任务,他会自动重复调用execute()方法 ThreadPoolExecutor.DiscardOldestPolicy()抛弃旧的任务 ThreadPoolExecutor.DiscardPolicy

26、()抛弃当前的任务,JDK线程工具线程池,关闭线程池public void shutdown()当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。此时不能向线程池中添加任何任务,否则抛出RejectedExecutionException异常,线程池等到当前所有任务(包括workQueue中的任务)都执行完成后退出。public List shutdownNow() 把线程池标记为STOP状态,不接收新任务,丢弃队列中等待的任务(方法返回未执行的队列中任务),同时调用正在执行中线程的interrupt方法来尝试中断线程。,JDK线程工具锁,不完美的synchronized 如果

27、某个线程试图获得锁,而这个锁已经被其他线程持有,那么没有办法回退,也没有办法在等待一段时间后放弃等待,或者在某个中断之后取消获取锁的企图,这些使得线程很难从活性问题中恢复。 没有办法改变锁的语义形式,例如重入性、读何写保护或者公平性等方面。 方法和块内的同步,使得只能够够对严格的块结构使用锁。例如:不能在一个方法中获得锁,而在另外一个方法中释放锁。 读取和写入操作都要上锁 性能开销java.util.concurrent.locks. Lock的增强 重入锁 ReentrantLockReentrantLock(boolean fair) 可以选择公平与否lockInterruptibly()

28、允许被打断的锁定tryLock()允许尝试锁定 读写锁 ReentrantReadWriteLock获得读锁条件:没有其他线程的写锁,没有写请求或者有写请 求,但调用线程和持有锁的线程是同一个获得写锁条件:没有其他线程的读锁 ,没有其他线程的写锁,CAS Compare and Swap(比较并交换) 我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。 synchronized是悲观锁,CAS是乐观锁java.util.concurrent.atomic.AtomicInteger类分析 public final int

29、getAndSet(int newValue) for (;) int current = get();if (compareAndSet(current, newValue)return current; public final boolean compareAndSet(int expect, int update) return pareAndSwapInt(this, valueOffset, expect, update);利用了cpu原语compareAndSet来保障值的唯一性,JDK线程工具锁机制,JDK线程工具1.6的synchronized,偏向锁(Biased locking)大概的实现如下:一个锁最初为NEUTRAL状态,当第一个线程加锁时,将该锁的状态修改为BIASED,并记录线程ID,当这一线程进行后续加锁操作时,若发现状态是BIASED并且线程ID是当前线程ID,则只设置一下加锁标志,不需要进行CAS操作。其它线程若要加这个锁,需要使用CAS操作将状态替换为REVOKE,并等待加锁标志清零,以后该锁的状态就变成DEFAULT,使用旧的算法处理,JDK线程工具其他,FutureTaskSemaphoreCountDownLatch CyclicBarrier,谢 谢!,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 网络科技 > Java

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报