多线程

首先 了解下进程和线程的区别:

进程:每个进程都有独立的代码空间和数据空间(进程上下文),进程间的切换会有很大的开销,一个进程包含1--n个线程

线程:同一类线程共享一块代码和数据空间,每个线程有独立的程序计数器和方法调用栈(简称方法栈)

在java中实现多线程的2中方法:继承Thread类、实现Runnable接口

 

java线程的生命周期:

当线程被启动过后,它既不是一启动就进入执行状态,也不是一直在执行状态,在java生命周期中,要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、死亡(Dead)五种状态;现在在创建以后,不可能一直霸占CUP独立运行,大部分时间处于运行和阻塞之间切换;

一、新建和就绪状态:

(1)用New关键字创建一个线程以后,该线程就进入了新建状态

(2)调用start方法以后就进入就绪状态,但不能立刻进入运行状态,要等到JVM的线程调度器调度

注意:只能对处于新建状态的线程调用start()方法,其他状态如果调用会报异常

二、运行和阻塞状态

(1)当处于就绪状态的线程获得cpu资源,jvm就会执行run方法,处于运行状态,当分配时间用完,又会进入就绪状态,等待下次分配cpu资源,

(2)当遇到以下几种 情况会进入阻塞状态:

<1>线程调用sleep()方法主动放弃cpu资源,

<2>线程调用IO式的阻塞方法

<3>等待某个通知wait

<4>调用suspend方法挂起

三、线程死亡

(1)run方法执行完

(2)线程抛出一个未捕获的异常

(3)直接调用stop方法,但可能会导致死锁

isAlive()方法判断该线程是否死亡(新建和死亡状态返回false)

 静态方法interrupted(),中断的状态由该方法清楚,第二次返回的是false

Thread和Runnable的区别:

如果一个类继承Thread,则不适合资源共享,如果一个类实现Runnable接口,则很容易实现资源共享

main方法也是一个线程,在java中所有的线程都是同时启动的 ,至于什么时候,哪个先执行,完全看谁先得到cpu资源

sleep()方法和wait()方法的区别:sleep是属于Thread类的,wait方法属于Object类的,调用后都会放弃cpu资源,进入阻塞状态,但是sleep()方法执行后线程不会丢锁,wait方法会丢锁,需要notify()方法唤醒

Thread.yield()//线程让步,暂停当前正在执行的线程对象,把机会让给同等优先级或更高优先级的线程,当前线程回到可运行状态

时不能保证这个线程不被再次选中(即无法保证达到让步的目的)。

yield()从未导致线程转到等待/睡眠/阻塞状态。大多数情况下是从运行状态到可运行状态,有时可能没效果。

sleep()和yield()的区别:

<1>sleep()使得当前线程进入阻塞状态,该线程在指定的时间内肯定不会被执行,yield()只是回到可运行状态,一旦获取到资源就会被执行

<2>sleep()方法被调用,释放cpu资源,允许较低优先级的线程获得机会,而yield()被调用时,只是让出cpu空间,进入可运行状态,

让同等优先级的线程被执行,

 

 

 

join()方法等待其他线程终止,在当前线程中调用另外一个线程的join()方法,则当前线程进入阻塞状态,直到另外一个线程结束,当前进程进入就绪状态 等待获取cpu资源,

在很多情况下,主线程生成并启动子线程,如果子线程存在大量耗时间的运算,主线程往往先于子线程结束,但是如果主线程完成其他事务后需要子线程的处理结果,也就是主线程要等到子线程完成后再结束,这个时候就要用到join()方法了

例如:在main方法里面创建2个线程后,start()全部启动,在分别调用这2个线程的join()方法,那么主线程(main)就会等待这2个线程完成后再结束,如果不加join方法,可能main主线程就会提前结束。

 线程调度器:线程调度器是一个后台操作系统服务,它负责为Runnable状态的线程分配Cpu时间,一旦我们创建一个线程并启动它,它便依赖于线程调度器的实现,时间分片是指分配给可用的Runnable线程的过程。分配cpu时间可以基于线程的优先级或者线程的等待时间,线程控制并不受到java虚拟机的控制,

为什么sleep()和yield()方法是静态的?

Thread的sleep()方法和yield() 方法都是指当前正在执行的线程上运行,所以处于其他等待状态的线程上调用这些方法时没有意义,并避免程序员错误的认为可以在其他非运行状态下调用这个方法

如何确保线程安全:

(1)同步

(2)volatile关键字:当我们使用这个关键字修饰变量时,所有线程都只是读取这个变量但不缓存,这就确保线程读取的变量在同一内存中是一致的

(3)使用原子类

(4)实现并发锁

(5)使用线程安全类

线程池:对于并发的线程量很多,并且每个线程都是执行很短的时间就结束了,这样频繁的创建就会降低系统的效率,因为频繁的创建和销毁线程要消耗时间

这时候就可以用线程池:

ThreadPoolExecutor

 

posted @ 2016-06-08 15:55  ding9587  阅读(205)  评论(0编辑  收藏  举报