其他分享
首页 > 其他分享> > 线程

线程

作者:互联网

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