(二) java并发编程--为什么使用多线程

来源:互联网 发布:珠海金山软件招聘 编辑:程序博客网 时间:2024/05/16 10:11

(1.1)计算机硬件

在计算机发展早期,计算机还没有操作系统;自始至终的执行一个程序,这个程序直接访问机器的所有资源。每次只运行一个程序,不能很好的利用稀缺的计算机资源

(1.2 )操作系统的诞生

操作系统的诞生让多个程序可以同时运行,程序在各自的进程(process)中运行,互相分离,各自独立运行,有操作系统来分配资源,比如内存、文件句柄、安全证书。如果需要的话,进程会通过一些原始的机制互相通信:socket、信号处理(singal handles)、共享内存(shared memory)、信号量(semaphores)和文件。

(1.3)单线程带来的问题

1.3.1)资源利用

有时候需要等待外部操作,比如等待输入和输出,单进程在等待的时候不能进行其他操作,如果在等待的过程中其他程序可以运行就会提高效率。

例如我们去沏茶,首先要烧水,我在烧水的时候可以准备茶壶、茶碗,而不是完全等水烧开了,再去做一些准备工作。其实是更好的利用了“自己”这个资源,不会让空闲下来,这样也会提高效率。

1.3.2)进程之间公平性

单进程,需要一个程序结束后,下一个程序才能进行,类似有“严格的等级制度”,不能很好的共享计算机资源,对系统没有平等的优先级别。

1.3.3)方便

写一些程序,让它们各自自行一个单独任务,要比把所有的程序都放到一个程序更容易理解。就像写代码的时候,把所有的业务逻辑都放到一个方法中一样,不容易理解,也难以修改和扩展。

 

(1.4)多线程优点

线程是通过异步的方式把工作流程转化为普遍存在的看似顺序流程“,使程序模拟人类工作和交互变得容易了。另一方面,它们可以把复杂、难以理解的代码转化为直接、简洁的代码,这样更容易读写和维护。

线程在GUI应用程序中是非常有用的,可以用来改进用户接口的响应性,并且在服务器应用中,提高资源的利用率和吞吐率。

上面介绍过,如果一个程序是单线程的,这个处理器在等待一个同步I/O操作完成的时候,仍然是空闲的。在一个多线程程序中,当第一个程序等待I/O结束的同时,另外的一个线程也可以运行,使用使得应用程序遇到I/O阻塞的时候仍然有进展。

(1.4.1)线程的风险

例如下代码;

packageunsafeThread;/***Created by fang on 2017/11/17.*/public classUnsafeSequenceimplements Runnable{    privateint value;    publicint getNext(){    returnvalue++;}public voidrun(){    System.out.println(getNext());} } packageunsafeThread; /*** test for thread*Created by fang on 2017/11/17.*/public classClient{   publicstatic void main(String[]args) {    UnsafeSequenceunsafeSequence =new UnsafeSequence();      for(inti=0;i<10000;i++){       Threadthread = newThread(unsafeSequence);       thread.start();    } }}


 


 

执行结果中可以看出有的数字并未是按照顺序输出,每个线程独立操作,读取这个值,加1,然后再写入新值,因为在操作过程中多个线程交替占用运行时,某个线程读取value值的时候,已经有线程再给value1,导致value并不是按照次序输出,这就是线程不安全。

 

活跃度失败

实在并发编程写代码的时候,可能会有这样的风险,可能会因为粗心导致程序无限循环。例如A线程等待一个线程B独占有的资源,B永远不释放这个资源,A将永远等待下去。

(1.5)线程无处在

 

 

每一个java应用程序都使用线程。当JVM启动后,它创建一些线程来进行自身的常规管理(垃圾回收、终结处理),以及运行一个main函数的主线程。

 

AWTSwing用户接口框架创建线程来管理用户接口事件。

 

Timer创建执行延迟的任务线程。

 

组件框架,比如servletRMI,会创建线程池,池中线程调用组件方法。

 

线程安全组件示例

ServletsjavaServer PagesJSPsServlets框架设计目的是,在处理web应用部署,分发来自远程HTTP客户请求这些基础层业务。一个请求到达Server并被分发后,可能通过一个过滤器链到达相应的Servlet或者JSP。每个servlet代表逻辑的一个组件,在访问较大网站中,许多客户可能对相同的servlet的服务请求。servlet的规范规定了一个Servlet必须为多个用户同时调用它做好准备。换句话说,Servlet需要是线程安全的。

即使能够保证一个servlet一次只被一个线程调用,你在建立一个web应用程序时可能仍然要注意线程安全。Servlets通常访问与其他Servlets共享的状态信息,比如程序范围内的对象(Servletcontext对象)或者Session范围内的对象。当一个Servlet访问的对象在Servlets间共享或者请求间共享时,必须对这些对象的访问控制进行适当的协调,因为来自不同线程的多个请求可能同时访问它们。ServletJSPServletFilter以及那些存储在ServletContextHttpSession容器中的对象,明显必须是线程安全的。

 

RMIRemote Method InvocationRMI使你能够调用在另外一个JVM上运行的对象方法。当你使用RMI调用一个远程方法时,这个方法的参数被打包成一个比特流,并穿越整个网络到达远程JVM,在那里会被解包并传递给远程方法。

RMI代码调用了你的远程对象时,这个调用发生在哪一个线程?你并不知道,但绝对不是你创建的那个线程--你的对象被RMI管理的一个线程调用。RMI创建了多少线程?在多个RMI线程中,同一个对象的同一个方法是不是可能同时被调用,所以RMI对象必须是线程安全的。

 

(1.6)总结

多线程是历史发展的必然,那线程的安全性怎么保障呢?什么是线程的安全性?下一篇线程的安全性。