1.优点与代价 优点:资源利用率好;程序响应快;在某些情况下程序设计更简单
代价:设计复杂;上下文切换的开销;增加资源消耗
2.线程状态分为6种,找了各种资料,发现是6种状态,详情可见java.lang.Thread 的State枚举:
2.1 初始(new):创建一个线程,但还没有调用start方法。
2.2 运行(runnable):java线程将ready(就绪)和running(运行中)状态笼统的合称为“运行” 。
2.2.1 其他线程(如main线程)调用start方法,当前线程进入可运行线程池,等待线程调度,分配CPU的使用权,此时线程处于 “就绪状态”。
(1)就绪状态只是说明有运行的资格,但是调度程序没有挑选到你,就永远只能是“就绪”状态。
(2)调用线程的start方法,线程进入“就绪”状态。
(3)当前线程sleep方法结束,其他线程join方法结束,某个线程拿到对象锁,等待用户输入完毕,这些线程进入“就绪”状态。
(4)当前线程时间片用完,调用当前线程的yield方法,进入“就绪”状态。
(5)锁池里的线程拿到对象锁后,该线程进入“就绪”状态。
2.2.2 当线程获得CPU的时间片后,线程调度程序从可用线程池中拿出它作为当前线程执行时,该线程变为“运行中”状态,这也是线程进入“运行中”状态的唯一方法。
2.3 阻塞(blocked):线程阻塞于锁(线程去获取别的线程正在使用的资源),线程进入synchronized修饰的方法或者代码块(获取锁)的状态。
2.4 等待(waiting):等待共享资源允许被使用或者其他线程执行完毕。进入该状态的线程需要其他线程进行一些特定动作(通知或者中断)。不会被分配CPU执行时间,需要显示唤醒,否则一直处于“等待”状态。
2.5 超时等待(timed_waiting):该状态不同于waiting,他可以在指定的时间后自动唤醒,调用sleep或者wait方法出现此状态。
2.6 死亡(dead):run方法正常退出或者main()方法完成,线程进入死亡状态。或者异常未被捕获,影响run方法执行,线程也会死亡。
死亡线程不可再复生,在一个终止的线程上调用start方法,会抛出java.lang.IllegalThreadStateException异常。
线程状态转换如图:
3.线程相关方法:
(1)Thread.sleep(long millis)–线程休眠
线程交出CPU,让CPU先去执行其他任务,给其他线程执行机会的最佳方式。
调用sleep方法,线程从运行状态进入休眠状态(阻塞),但是不释放锁。即当前线程持有某个对象锁时,即使调用sleep方法,其他线程也无法访问这个对象。
sleep方法结束后,线程从阻塞变成就绪状态(可运行)。
(2)obj.wait()/obj.wait(long millis):
当前线程调用对象的wait方法,当前线程释放对象锁,进入等待队列,通过notify/notifyall或者wait(long millis)millis时间自动唤醒。
(3)Thread.yield():
当前线程调用此方法,表示当前线程放弃使用CPU时间片,由“运行中”状态变“就绪状态”,但是不释放锁资源,让OS重新选择线程执行。
作用:让出CPU,让同等优先级的其他线程轮流执行,但是不保证,因为线程调度程序还可能选中当前线程。该方法不会造成“阻塞”状态。
与sleep类似,但是不能由用户设置暂停多长时间。
(4)Thread.join()/Thread.join(long millis):
当前线程调用其他线程t的join方法,当前线程进入waiting/timed_waiting方法,不释放已经持有的锁资源。
线程t执行完毕或者到时后,线程进入runnable状态,也有可能进入blocked状态,因为join方法是基于wait方法实现的。
以上四个方法,只有wait释放锁资源,其余均不释放。 wait和notify方法都是obj下面的方法。(5)obj.notify()唤醒在该对象监视器上的等待的单个线程,选择是随意的。obj.notifyall()唤醒该对象监视器上等待的所有线程。
(6)LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 当前线程进入waiting/timed_waiting状态。
对比wait方法,不需要获得锁就可以让线程进入waiting/timed_waiting状态,需要通过LockSupport.unpark(Thread thread)唤醒。
4.多线程实现方式: (1)继承Thread类:无返回值 (2)实现Runnalbe接口:无返回值 (3)实现Callable接口:有返回值 (4)使用ExecutorService、Callable、Future:有返回值
实现有返回结果的线程:有返回值的线程必须实现callable接口,执行callable接口返回一个future对象,调用future.get()方法,可得到线程的返回值。
注意:
future.get()方法是阻塞的,调用该方法之后,线程会一直阻塞直到得到所有的返回结果
如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
例:
ExecutoreService提供了submit()和execute()方法,二者区别:
submit()方法:传递一个Callable(或Runnable)类型,返回Future;
execute()方法:参数是Runnble类型,返回值是void。
5.线程中的锁synchronized和lock区别:
(1)synchronized是java中的关键字,是java的内置特性,而lock不是,他是一个类,通过这个类实现同步访问。
(2)synchronized不需要主要释放锁,方法或者代码块执行完毕,系统会让线程主动释放锁资源。而lock需要主动释放,否则会造成死锁现象。
(3)synchronized不知道线程是否获取到锁,而lock的锁可知道当前线程是否成功获取到锁。。