一、线程的实现
两种方式:
1.直接实现Thread类
2.继承Runnable接口
上面两种方法都要实现run()方法,最终都会生成一个Thread对像;一般推荐使用第二种办法,因为线程只是实现了Runnable接口,还可以继承其他类。很适合多个相同线程处理同一份资源,能很好的将cpu、代码和数据分开,形成清晰模型,很好的体现了面向对象编程的思想.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
关于inheritableThreadLocals和ThreadLocals的区别,通过源码可以看到,父线程的inheritableThreadLocals的内容会被子线程复制一份继承。而ThreadLocals是不会这样做的。
二、 线程的状态及属性
1. 线程状态:
1) 新建(new):线程被创建后的初始状态
2) 就绪(runnable):调用start()方法,进入“就绪”状态
3) 运行(running):进入“就绪”状态后,获取到cpu分配时间进入“运行状态”
4) 阻塞/等待(blocked/*waiting):
a.线程需要等待某个条件时,而条件未成熟时,等待调度器通知,进入‘等待’状态
b.获取某个资源时,资源被加锁,等待资源被释放,进入‘阻塞’状态
5) 死亡(terminated):run()执行完毕或因异常导致退出,线程死亡
2. 线程重要属性:
1) 优先级
在Java中,每个线程都会拥有一个默认的优先级(5),如果未定义,则会继承父类的优先级,也通过setPriority(int)设置(1-10之间)。
注意:优先级高的并不一定会先执行,cpu尽量将资源分配给优先级高的,即优先级高的执行几率要大很多,但并不是等优先级高执行完后才开始执行优先级低的,具有一定的随机性。
2) 守护线程
在java中线程一般分为用户线程user和守护线程Daemon。它们在其他方面基本相同,但唯一的区别在于虚拟器中只剩下守护线程时,就可以退出了。
守护线程是一种比较低级别的线程,一般用于为其他类别线程提供服务,因此当其他线程都退出时,它也就没有存在的必要了,例如jvm中的垃圾回收线程。可以通过setDaemon(boolean) 设置线程的Daemon模式。
三、 线程涉及的常用方法
1. start方法
线程对象被创建后,通过start方法启动,此时线程会被放置到等待队列中且状态由‘新建’进入‘就绪’,等待调度器分配资源,随时进入到‘运行’状态。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
2. run方法
线程被启动后,每当获取到资源,jvm就会去调用run方法,run执行结束,则线程结束。run只是一个普通的方法,存放业务逻辑。
3. wait - notify - notifyAll方法
这三个方法为java.lang.Object类为线程提供的用于实现线程间通信的同步控制方法。因此,可以说每一个对象实例都存在一个等待队列。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
1) obj.wait() : 相当于wait(0),线程释放obj对象的monitor(对象监视器),即交出互斥锁,变为“等待”状态,进入‘等待队列’,必须由通信线程唤醒。
2)wait(M>0) :线程交出互斥锁后被挂起,变为“等待”状态,进入‘等待队列’,必须由通信线程唤醒或至多等待M毫秒后唤醒自身。
3)notify() :随机唤醒阻塞在obj对象上的一个线程,退出“等待状态”,然后线程再尝试获取互斥锁,获取成功则继续执行,失败则进入‘阻塞队列’中。
4)notifyAll():唤醒所有阻塞在obj对象上的线程,即所有线程都退出“等待状态”,一起竞争再尝试获取互斥锁,获取成功则继续执行,其他失败则进入‘阻塞队列’中。
4. join方法
join方法本质上还是利用wait的来实现,t.join()即为当前主线程释放了t对象的monitor,进入到t对象阻塞队列中,巧妙的将t线程的执行达到了同步执行的效果。
1)t.join(M=0):和t.join()效果相同。主线程到此处时进入阻塞状态,直到thread执行完毕,才会继续执行。
2)t.join(M>0): 主线程到此处时进入阻塞状态,直到thread执行完毕或至多等待M毫秒后,才会继续执行。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
通过以上可以看到,一般最好不要主动对Thread对象使用t.wait(), t.notify() ,t.notifyAll() 方法,否则可能会引起一些不必要问题。
5. park - parkNanons - unpark方法
LockSupport是JDK中比较底层的类,用来创建锁和其他同步工具类的基本线程阻塞原语。我们在阅读java锁和同步队列器AbstractQueuedSynchronizer中可以看到,就是通过LockSupport.park()和LockSupport.unpark(thread)来阻塞和唤醒的。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
另外这里要重点分析一下 park/unpark和wait/notify 方法的的区别为:
1)park/unpark 针对的是一个“许可(permit)”的授权,unpark可以先执行,当另一个线程执行到park时,发现已经被“许可”了,则继续执行。注意:这个“许可”是不能叠加的,“许可”是一次性的。即便先多次执行unpark,也只相当于一次。
2)线程B要用notify通知线程A,那么线程B要确保线程A已经在wait调用上等待了,否则线程A可能永远都在等待
park/unpark模型真正解耦了线程之间的同步,线程之间不再需要一个Object或者其它变量来存储状态,不再需要关心对方的状态。
6. interrupt方法
一般来说,当线程调用thread.sleep()、obj.wait()、thread.join()处于阻塞状态时,此时再调用thread.interrupt(), thread会立即抛出InterruptedException异常,并清除“interrupt”状态。
而对于locksuport.park()方法来说,它会响应thread.interrupt()方法提前退出阻塞,但不会抛出InterruptedException异常及清除“interrupt”状态,使用者可自行对“interrupt”状态进行处理。
1)interrupt() 设置线程的状态为“中断”状态
2)interrupted() 返回线程是否“中断”状态,并立即重置状态
3)isInterrupted 线程是否为“中断”状态
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
7. sleep方法
执行当前方法后,当前线程暂停指定毫秒数,进入‘等待’状态,这段时间内cpu不会再执行它。注意的是,它并不会交出互斥锁。
- 1
- 2
- 1
- 2
8. yeid方法
执行当前方法后,线程从‘运行’状态变为‘就绪’状态,同时立马和其他线程一起竞争CPU,有可能还是它自身继续执行。当然它也不会交出互斥锁。
- 1
- 2
- 1
- 2
http://blog.csdn.net/lh513828570/article/details/60144598