最近面试去了,发现好多人都问我对多线程的理解,十分尴尬,好多都忘了,会写不会说,感觉自己要被淘汰啦 哈哈,在网上找了多个文档,针对多线程的创建 项目中的体现,,悲观锁乐观锁的应用做了一个归纳吧,有一些是我直接看了后直接复制的,有的可能没注明出处,麻烦作者看见了联系我,
多线程的简单理解
明白什么是多线程,通俗一点的理解为:在一个程序里,我想同时让这个程序完成多个任务。
比如:让主函数 main 在打印1~100之间的所有整数的时候,要求在主函数打印到 20 的时候,再运行另一个类里的程序,让它打印10~100之间的所有整数。
这里忽略同进程内的多线程之间的抢占时间问题,上面的举例需求是要求一个程序只要有发生同时运行俩个程序的情况就行,即不准出现无论程序跑多少次都是先把 main 函数的1~100 的所有整数打印完再打印10~100 之间的所有整数(PS:如果是这样的话,那就是 main 调用其他类的方法,属于单线程了)。
线程在项目中的运用及体现
一个请求就是一个线程,这个线程不需要我们来控制,WEB容器自己实现,这是第一个应用。
我们也可以在web中new线程来做我们的事。比如,当我有一个很耗时的操作,像统计排名之类的功能。当用户在web上点击排名时,由于这个计算量太大,可能要计算3~5分钟或更久。这时我们会在servlert中new一个线程来做这个事情,这样用户就可以点击排名后去做其他的事,等排名出来现通知他。如果不new线程当用户点排名时,浏览器就会一直卡在这里,一个圈圈转啊转的,就是不出来。所以我们可以new线程来做耗是任务。还有很多比如,定时任务、WEB版的爬虫程序、监听等
Java多线程的悲观锁与乐观锁
一:悲观锁
http://www.cnblogs.com/ygj0930/p/6561376.html
悲观锁,就是不管是否发生多线程冲突,只要存在这种可能,就每次访问都加锁,加锁就会导致锁之间的争夺,有争夺就会有输赢,输者等待。
syncrhoized是一种独占锁,即:占用该锁的线程才可以执行,申请该锁的线程就只能挂起等待,直到占用锁的线程释放锁才唤醒,拿到锁并执行。由于在进程挂起和恢复执行过程中存在着很大的开销,并且当一个线程正在等待锁时,它不能做任何事。所以syncrhoized是一种悲观锁,凡是用syncrhoized加了锁的多线程之间都会因锁的争夺结果导致挂起、唤醒等开销。
二:乐观锁
获得锁后一直持有锁以防本线程再次申请该锁造成无谓的解锁再加锁开销,或者假设没有冲突而去完成同步代码块如果冲突再循环重试,或者采取申请锁失败后不立刻挂起而是稍微等待再次尝试获取 等待策略,以减少线程因为挂起、阻塞、唤醒(发生CPU的调度切换) 而造成的开销。
偏向锁、轻量级锁(CAS轮询)、自旋锁 就是基于上述思路的乐观锁。
在多线程的加锁机制中,JVM会首先尝试乐观锁,失败后才调用悲观锁。
二、创建多线程的三种方式
第一种方式:
创建:编写一个类 MyThread1 (这里只是小生的自定义命名,方便与代码里名称一致)让它继承 Thread 类,
并把需要多线程运行的程序放到 public void run() 方法里。
启动:在主函数中,new 出 MyThread1 类的实例。
运行:调用 MyThread1 类的实例的 start() 方法即可。
第二种方式:
创建:编写一个类 MyThread2 让它实现 Runnable 接口,并且要重写 run() 方法(把需要多线程运行的程序放到 public void run() 方法里)。
启动:在主函数中,new 出 MyThread1 类的实例,
new 出Thread 类(带有 target 的构造方法),
把MyThread1 类的实例作为参数传入Thread 类的构造方法里。
运行:调用 Thread 类的实例的 start() 方法即可。
第三种方式:
创建:实现 Callable 接口(小生定义这个类为 MyCallable),并且实现 call() 方法,注意 call() 方法是有返回值的。
启动:new 出Callable 接口的实现类MyCallable,
new 出 FutureTask 类的实例 task,
把call() 方法的返回值放入FutureTask 类的构造方法里,
把 task 放入 new 出的 Thread 构造方法里。
运行:调用 Thread 类的实例的 start() 方法即可。
三种方式的利弊总结:
第一种方法好处:创建已经继承 Thread 类的类的实例,调用 start() 方法就能运行,代码写起来很方便,
当然缺点就是继承了Thread 父类,就没有办法继承其他的类了,扩展性不好。所以一般不建议用这种方式创建多线程。
第二种方法好处:继承接口,扩展性好。
弊端是相对的,第一种方式不好也是相对于第二种来说的,而第二种方式相比第三种方式来说,run() 方法没有返回值,并且不能申明抛出异常。
第三种方式好处上面已经说明,不好的就是编码很麻烦