JAVA线程(一)

来源:互联网 发布:4y4淘宝店铺装修 编辑:程序博客网 时间:2024/05/22 14:51

对于JAVA来说,尽管语言的内部提供了多线程的支持,但是JAVA多线程的应用开发仍会遇到很多问题,首先是很难编写正确,其次是很难测试是否正确,最后是出现了问题很难调试。


JAVA线程的基本概念:

在操作系统中两个比较容易混淆的概念是进程(process)和线程(thread)。

进程:进程是资源的组织单位。进程有一个包含了程序内容和数据的地址空间,以及其它的资源,包括打开的文件、子进程和信号处理器等。不同进程的地址空间是互相隔离的。

线程:而线程表示的是程序的执行流程,是CPU调度的基本单位。线程有自己的程序计数器、寄存器、栈和帧等。

引入线程的动机在于操作系统中阻塞式I/O的存在。当一个线程所执行的I/O被阻塞的时候,同一进程中的其它线程可以使用CPU来进行计算。这样的话,就提高了应用的执行效率。


创建线程:

1.继承java.lang.Thread类并覆写其中的run()方法

2.在创建java.lang.Thread类的对象的时候,在构造函数中提供一个实现了java.lang.Runnable接口的类的对象,在得到了java.lang.Thread类的对象之后,通过调用其start()方法就可以启动这个线程的执行


线程的状态:(妈蛋,笔者在某个电面中被问到了这个问题,竟然紧张没答上来......)

1.可能正在占用CPU 时间运行(运行态)

2.可能处在就绪状态,等待被调度执行(就绪态)

3.可能阻塞在某个资源或是事件上(阻塞态)

多线程程序中的逻辑不能依赖于CPU 的调度算法


可见性:

可见性(visibility)的问题是Java 多线程应用中的错误的根源。在一个单线程程序中,如果首先改变一个变量的值,再读取该变量的值的时候,所读取到的值就是上次写操作写入的值。也就是说前面操作的结果对后面的操作是肯定可见的。但是在多线程程序中,如果不使用一定的同步机制,就不能保证一个线程所写入的值对另外一个线程是可见的。


JAVA中的锁:

当数据竞争存在的时候,最简单的解决办法就是加锁。锁机制限制在同一时间只允许一个线程访问产生竞争的数据的临界区。Java 语言中的 synchronized 关键字可以为一个代码块或是方法进行加锁。任何Java 对象都有一个自己的监视器,可以进行加锁和解锁操作。

synchronized关键字的使用可以参考笔者的另外一篇博客:

http://blog.csdn.net/kameleon2013/article/details/18778735


JAVA线程的同步:

在有些情况下,仅依靠线程之间对数据的互斥访问是不够的。有些线程之间存在协作关系,需要按照一定的协议来协同完成某项任务,比如典型的生产者-消费者模式。
这种情况下就需要用到Java提供的线程之间的等待-通知机制。当线程所要求的条件不满足时,就进入等待状态;而另外的线程则负责在合适的时机发出通 知来唤醒等待中的线程。Java中的java.lang.Object类中的wait/notify/notifyAll方法组就是完成线程之间的同步的。

在某个Java对象上面调用wait方法的时候,首先要检查当前线程是否获取到了这个对象上的锁。如果没有的话,就会直接抛出java.lang.IllegalMonitorStateException异常。如果有锁的话,就把当前线程添加到对象的等待集合中,并释放其所拥有的锁。当前线程被阻塞,无法继续执行,直到被从对象的等待集合中移除。引起某个线程从对象的等待集合中移除的原因有很多:对象上的notify方法被调用时,该线程被选中;对象上的notifyAll方法被调用;线程被中断;对于有超时限制的wait操作,当超过时间限制时;JVM内部实现在非正常情况下的操作。

从上面的说明中,可以得到几条结论:

1.wait/notify/notifyAll 操作需要放在synchronized代码块或方法中,这样才能保证在执行 wait/notify/notifyAll的时候,当前线程已经获得了所需要的锁。

2.当对于某个对象的等待集合中的线程数目没有把握的时候,最好使用 notifyAll 而不是notify。notifyAll虽然会导致线程在没有必要的情况下被唤醒而产生性能影响,但是在使用上更加简单一些。

3.由于线程可能在非正常情况下被意外唤醒,一般需要把wait 操作放在一个循环中,并检查所要求的逻辑条件是否满足。


中断线程:

通过一个线程对象的interrupt()方法可以向该线程发出一个中断请求。中断请求是一种线程之间的协作方式。当线程A通过调用线程B的interrupt()方法来发出中断请求的时候,线程A 是在请求线程B的注意。线程B应该在方便的时候来处理这个中断请求,当然这不是必须的。当中断发生的时候,线程对象中会有一个标记来记录当前的中断状态。通过isInterrupted()方法可以判断是否有中断请求发生。如果当中断请求发生的时候,线程正处于阻塞状态,那么这个中断请求会导致该线程退出阻塞状态。可能造成线程处于阻塞状态的情况有:

1.当线程通过调用wait()方法进入一个对象的等待集合中,或是通过sleep()方法来暂时休眠,或是通过join()方法来等待另外一个线程完成的时候。

2.在线程阻塞的情况下, 当中断发生的时候, 会抛出java.lang.InterruptedException,代码会进入相应的异常处理逻辑之中。实际上在调用wait/sleep/join方法的时候,是必须捕获这个异常的。中断一个正在某个对象的等待集合中的线程,会使得这个线程从等待集合中被移除,使得它可以在再次获得锁之后,继续执行java.lang.InterruptedException异常的处理逻辑。

通过中断线程可以实现可取消的任务。在任务的执行过程中可以定期检查当前线程的中断标记,如果线程收到了中断请求,那么就可以终止这个任务的执行。当遇到java.lang.InterruptedException 的异常,不要捕获了之后不做任何处理。如果不想在这个层次上处理这个异常,就把异常重新抛出。当一个在阻塞状态的线程被中断并且抛出java.lang.InterruptedException 异常的时候,其对象中的中断状态标记会被清空。如果捕获了java.lang.InterruptedException 异常但是又不能重新抛出的话,需要通过再次调用interrupt()方法来重新设置这个标记。


0 0