ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

线程

2021-07-12 10:04:15  阅读:157  来源: 互联网

标签:状态 对象 封闭 线程 2.3 2.5


2.1线程的生命周期: 新建状态(New): 当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable): 当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行; 运行状态(Running): 当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。 注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中; 阻塞状态(Blocked): 处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种: 1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态; 2.同步阻塞--线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态; 3.其他阻塞--通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。 死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。 2.2图解: 2.3Java多线程的创建及启动 ①继承Thread类,重写该类的run()方法。 代码示例: 如上所示,继承Thread类,通过重写run()方法定义了一个新的线程类MyThread,其中run()方法的方法体代表了线程需要完成的任务,称之为线程执行体。当创建此线程类对象时一个新的线程得以创建,并进入到线程新建状态。通过调用线程对象引用的start()方法,使得该线程进入到就绪状态,此时此线程并不一定会马上得以执行,这取决于CPU调度时机。 ②实现Runnable接口,并重写该接口的run()方法,该run()方法同样是线程执行体,创建Runnable实现类的实例,并以此实例作为Thread类的target来创建Thread对象,该Thread对象才是真正的线程对象。 代码示例: ③使用Callable和Future接口创建线程。具体是创建Callable接口的实现类,并实现clall()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。 2.4sleep()、wait()、join()、yield()方法的区别: 2.5线程安全 2.5.1线程不安全产生原因: 2.5.2保证线程安全的方式: 2.5.2.1什么是线程安全? 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在调用代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为获取正确的结果(单线程下的结果和多线程下获得的保持一致),那么就称这个类是线程安全的。 2.5.2.2怎么才能做到线程安全? 1.基于JVM的锁   无法解决分布式情况的问题 2.基于数据库的锁(分布式)   耗费资源 3.基于redis的锁(分布式)   可能会出现死锁 4.基于zookeeper的锁(分布式) 最优级   2.5.2.3避免并发的方式: 2.5.2.3.1线程封闭 什么是线程封闭? 就是把对象封装到一个线程里,只有这一个线程能看到此对象。那么这个对象就算不是线程安全的也不会出现任何安全问题。 实现线程封闭有哪些方法? ①ad-hoc 线程封闭 这是完全靠实现者控制的线程封闭,他的线程封闭完全靠实现者实现。 Ad-hoc 线程封闭非常脆弱,应该尽量避免使用。 ②栈封闭 栈封闭是我们编程当中遇到的最多的线程封闭。 什么是栈封闭呢? 简单的说就是局部变量。 多个线程访问一个方法,此方法中的局部变量都会被拷贝一份到线程栈中。所以局部变量是不被多个线程所共享的,也就不会出现并发问题。所以能用局部变量就别用全局的变量,全局变量容易引起并发问题。 2.5.2.3.2无状态的类 没有任何成员变量的类,就叫无状态的类,这种类一定是线程安全的。 无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象.不能保存数据,是不变类。 如果这个类的方法参数中使用了对象,也是线程安全的吗?比如: 当然也是,为何?因为多线程下的使用,固然 user 这个对象的实例会不正常,但是对于 StatelessClass 这个类的对象实例来说,它并不持有 UserVo 的对象实例,它自己并不会有问题,有问题的是 UserVo 这个类,而非 StatelessClass 本身。 2.5.2.3.3让类不可变 ①加final关键字,对于一个类,所有的成员变量应该是私有的,同样的只要有可能,所有的成员变量应该加上final关键字,但是加上final,要注意如果成员变量又是一个对象时,这个对象所对应的类也要是不可变,才能保证整个类是不可变的。 ②根本就不提供任何可供修改成员变量的地方,同时成员变量也不作为方法的返回值。 但是要注意,一旦类的成员变量中有对象,上述的 final 关键字保证不可变并不能保证类的安全性,为何?因为在多线程下,虽然对象的引用不可变,但是对象在堆上的实例是有可能被多个线程同时修改的,没有正确处理的情况下,对象实例在堆中的数据是不可预知的。这就牵涉到了如何安全的发布对象这个问题。 2.5.2.3.4加锁和CAS ①synchronized synchronized关键字,就是用来控制线程同步的,保证我们的线程在多线程环境下,不被多个线程同时执行,确保数据的完整性, ②Lock Lock是在Java1.5被引入进来的,Lock的引入让锁有了可操作性,我们在需要的时候去手动的获取锁和释放锁,甚至我们还可以中断获取以及超时获取的同步特性。 测试: 结果: 进入方法我们首先要获取到锁,然后去执行我们业务代码,这里跟synchronized不同的是,Lock获取的所对象需要我们亲自去进行释放,为了防止我们代码出现异常,所以我们的释放锁操作放在finally中,因为finally中的代码无论如何都是会执行的。 其实在Lock还有几种获取锁的方式,这里再说一种,就是tryLock()这个方法跟Lock()是有区别的,Lock在获取锁的时候,如果拿不到锁,就一直处于等待状态,直到拿到锁,但是tryLock()却不是这样的,tryLock是有一个Boolean的返回值的,如果没有拿到锁,直接返回false,停止等待,它不会像Lock()那样去一直等待获取锁。同时还可以设置等待时间。 2.5.2.3.5安全的发布 类中持有的成员变量,如果是基本类型,发布出去,并没有关系,因为发布出去的其实是这个变量的一个副本. 但是如果类中持有的成员变量是对象的引用,如果这个成员对象不是线程安全的,通过 get 等方法发布出去,会造成这个成员对象本身持有的数据在多线程下不正确的修改,从而造成整个类线程不安全的问题。 2.5.2.3.6ThreadLocal ThreadLocal 是实现线程封闭的最好方法。   ThreadLocal 内部维护了一个 Map,Map 的 key 是每个线程的名称,而 Map 的值就是我们要封闭的对象。每个线程中的对象都对应着 Map 中一个值,也就是 ThreadLocal 利用 Map 实现了对象的线程封闭。 2.5.2.3.7volatile 并不能保证类的线程安全性,只能保证类的可见性,最适合一个线程写,多个线程读的情景。

标签:状态,对象,封闭,线程,2.3,2.5
来源: https://www.cnblogs.com/CV-master/p/15000791.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有