Java线程常用方法
Thread类相关的方法
java.lang.Thread类包含了一些常用的方法,通过这些方法,我们可以对线程进行方便的操作。
但是这些方法中,有一些被废弃了如:stop(), stop(Throwable) ,suspend(), destroy() ,resume()。
常用的有如下方法:
start()
sleep(long millis)
join()
join(long millis)
interrupt()
yield()
方法声明如下
public synchronized void start() {}
public static native void sleep(long millis) throws InterruptedException;
public final void join() throws InterruptedException
{
join(0);
}
public final synchronized void join(long millis) throws InterruptedException
{}
public void interrupt() {}
public static native void yield();
废弃的
public final void stop() {}
public final synchronized void stop(Throwable
obj) {}
public final void suspend() {}.
public void destroy() {}
public final void resume() {}
Object类相关的方法
常用方法
notify();
notifyAll();
wait(long timeout)
wait();
方法声明如下
先看下线程的声明周期
Java线程的状态有6种
1 NEW新建状态
2 RUNNABLE 可运行状态(细分:1、READY就绪状态;2、RUNNING状态)
3 BLOCKED 阻塞状态
4 WAITING 等待状态
5 TIMED_WAITING 超时等待
6 TERMINATED 终止状态
start() ---启动线程
启动线程,新建状态的线程调用start();方法就会进入可运行状态。
wait()/wait(long millis) --- 线程等待
线程等待,调用wait()/wait(long millis)方法,线程暂时让出CPU使用权,同步锁让出。
wait()
public final void wait() throws InterruptedException {
wait(0);
}
源码注释
/*** Causes the current thread to wait until another thread invokes the
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object.
* In other words, this method behaves exactly as if it simply
* performs the call {@code wait(0)}.
* <p>
* The current thread must own this object's monitor. The thread
* releases ownership of this monitor and waits until another thread
* notifies threads waiting on this object's monitor to wake up
* either through a call to the {@code notify} method or the
* {@code notifyAll} method. The thread then waits until it can
* re-obtain ownership of the monitor and resumes execution.
* <p>
* As in the one argument version, interrupts and spurious wakeups are
* possible, and this method should always be used in a loop:
* <pre>
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait();
* ... // Perform action appropriate to condition
* }
* </pre>
* This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
翻译一下
/**
*使当前线程等待,直到另一个线程调用
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object.
*换句话说,这个方法的行为就好像它只是执行调用{@code wait(0)}。
*当前线程必须拥有此对象的监视器。线程释放此监视器的所有权并等待另一个线程。通知在此对象的监视器上等待唤醒的线程
*通过调用{@code notify}方法或{@code notifyAll}方法。然后,线程会一直等到可以重新获得监视器的所有权并恢复执行。
*在单参数版本中,中断和虚假唤醒,可能,并且此方法应始终在循环中使用:
* <pre>
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait();
* ... // Perform action appropriate to condition
* }
* </pre>
*此方法只能由作为所有者的线程调用这个对象的监视器。请参见{@code notify}方法
*描述线程可以成为的所有者的方式监视器。
总结:
(1)调用wait()声明在Object类中的非native本地方法,调用本地方法wait(long millis)。
(2)调用wait(long millis)方法而处于等待状态中的线程,JVM会把该线程放入等待池中,让出CPU使用权(wait()会释放持有的锁);
(3)调用wait(long millis),需要通过其他线程调用notify()或者notifyAll()方法唤醒当前等待中的线程,或者等待限制时间结束后,才有机会获取CPU使用权,可以进入其他状态。
wait(long millis)
public final native void wait(long timeout) throws InterruptedException;
总结:
(1)调用wait(long millis)声明在Object类中的final本地native方法,不可重写,而且必须由锁对象调用。
(2)调用wait(long millis)方法而处于等待状态中的线程,JVM会把该线程放入等待池中,让出CPU使用权(wait()会释放持有的锁);(3)调用wait(long millis),需要通过其他线程调用notify()或者notifyAll()方法唤醒当前等待中的线程,或者等待限制时间结束后,才有机会获取CPU使用权,可以进入其他状态。
(4)通过notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁。 只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为别人锁还没释放。
notify()/notifyAll() ---线程唤醒
线程唤醒,通过notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁。 只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为别人锁还没释放。
public final native void notify();
public final native void notifyAll();
* Wakes up a single thread that is waiting on this object's
* monitor. If any threads are waiting on this object, one of them
* is chosen to be awakened. The choice is arbitrary and occurs at
* the discretion of the implementation. A thread waits on an object's
* monitor by calling one of the {@code wait} methods.
* <p>
* The awakened thread will not be able to proceed until the current
* thread relinquishes the lock on this object. The awakened thread will
* compete in the usual manner with any other threads that might be
* actively competing to synchronize on this object; for example, the
* awakened thread enjoys no reliable privilege or disadvantage in being
* the next thread to lock this object.
* <p>
* This method should only be called by a thread that is the owner
* of this object's monitor. A thread becomes the owner of the
* object's monitor in one of three ways:
* <ul>
* <li>By executing a synchronized instance method of that object.
* <li>By executing the body of a {@code synchronized} statement
* that synchronizes on the object.
* <li>For objects of type {@code Class,} by executing a
* synchronized static method of that class.
* </ul>
* <p>
* Only one thread at a time can own an object's monitor.
翻译一下
*唤醒正在等待此对象的监视器。如果有线程正在等待此对象,则其中之一被选择被唤醒。选择是任意的,发生在执行的自由裁量权。线程等待对象的通过调用{@code wait}方法之一进行监视。
*唤醒的线程在当前线程放弃对该对象的锁定。唤醒的线将以通常的方式与其他线程竞争
*主动竞争以在此对象上同步;
例如,觉醒的线程在存在中没有可靠的特权或劣势锁定此对象的下一个线程。
*此方法只能由作为所有者的线程调用这个对象的监视器。线程成为对象的监视器有以下三种方式之一:
1、执行该对象的同步实例方法。
2、通过执行{@code synchronized}语句的主体在对象上同步的。
3、对于{@code Class,}类型的对象,通过执行该类的同步静态方法。
*一次只能有一个线程拥有一个对象的监视器。
总结:
(1)notify是Object的本地final方法,无法被重写, 用来唤醒线程。
唤醒的是那个线程?
(1)notify唤醒的是其所在锁所阻塞的线程,选择哪个线程取决于操作系统对多线程管理的实现。wait()和notify()配合使用
Java多线程常用到wait()和notify()方法来实现线程间的协作1. A线程取得锁,执行wait(),释放锁;
2. B线程取得锁,完成业务后执行notify(),再释放锁;
3. B线程释放锁之后,A线程取得锁,继续执行wait()之后的代码;
sleep(long millis) ---线程休眠
线程休眠,调用sleep(long millis)方法,线程暂时让出CPU使用权,但锁是不让出的(同步锁,每个对象有唯一的锁,拿不到锁,线程就无法调用)。
public static native void sleep(long millis) throws InterruptedException;
源码注释
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers. The thread does not lose ownership of any monitors.翻译一下
使当前正在执行的线程休眠(暂时停止)在指定的毫秒数内系统定时器和调度器的精度和准确性。线程不会失去任何监视器的所有权。
总结:
(1)sleep(long millis) 在Thread类中的静态方法,是一个native本地方法。
(2)调用sleep(long millis)的线程休眠millis毫秒,调用线程会暂时让出CPU的执行权,期间不参与CPU的调度,休眠millis毫秒。
(3)
调用sleep(long millis)的线程持有的锁是不让出的。时间到了会正常返回,线程就会处于就绪状态,然后参与CPU调度,获取到CPU资源之后就可以进入运行状态。
(4)sleep(long millis) 中指定的时间是线程不会运行的最短时间。
sleep(long millis) 方法不能保证该线程睡眠到期后就开始执行。
(5)
sleep(long millis) 醒来之后是立即执行(立即执行说的sleep(long millis)是不需要进行抢锁操作,并不是立即进入运行状态,而是进入可运行状态,首先进入就绪状态,抢到CPU的执行权才能进入运行状态。),而wait()被唤醒之后还需要再抢到锁之后执行。
例如:在main方法中,线程A调用sleep(2000)休眠2s,这2s内其他线程如main(或其他线程)可以运行,线程A不参与竞争,2s过后,线程A可以和线程main等线程公平的争夺CPU的使用权,获取后才能进入运行状态。
以下照抄自
https://blog.csdn.net/wxwzy738/article/details/8628443
sleep就是正在执行的线程主动让出cpu,cpu去执行其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。
wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行。
只有其他线程调用了notify方法(notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放。如果notify方法后面的代码还有很多,需要这些代码执行完后才会释放锁,可以在notfiy方法后增加一个等待和一些代码,看看效果),调用wait方法的线程就会解除wait状态和程序可以再次得到锁后继续向下运行。
sleep()和wait()方法的区别:
1,sleep()声明在Thread类中的静态方法;wait()声明在Object类中,而且必须由锁对象调用。(两个方法带参数的都是本地方法,wait()不带参数的调用的是带参数的本地方法)
2、sleep()等待的时候,不会释放锁;wait()等待过程中释放锁(同步锁,每个对象有唯一的锁)。
3、wait()和notify()必须在同步中调用(不然会报错InterruptedException)。
Join()/join(long millis) ---线程插队
线程插队功能,调用join的线程,优先运行,其他线程等待。join()
public final void join() throws InterruptedException {源码注释:Waits for this thread to die. 等待这个线程死。
join(0);
}
总结
(1)当插队线程A调用join方法后,调用start方法的线程(其他线程B、C等)需要等待,直到该线程执行完(调用join()的线程,即插队线程A),其他线程才有机会获取CPU使用权,才可以执行。
例如:main方法中,有一个线程A,调用strat(),然后马上join(),那么,main线程就该等待,直到线程A执行完毕,main才可以执行。
join(long millis)
public final synchronized void join(long millis) throws InterruptedException {}源码注释:Waits at most {@code millis} milliseconds for this thread to die. A timeout of {@code 0} means to wait forever.
此线程最多等待{@code millis}毫秒死。{@code=0}的超时意味着永远等待。
总结
(1)join(long millis)方法是一个同步方法。
(2)此线程(调用join(long millis)的)等待millis 毫秒后终止线程,假如这段时间内该线程还没执行完,那么其他线程结束等待,多个线程并发(交替的)执行;
(3)millis参数等于0时候,相当于join()无参方法,直到该线程执行完。
假如:main方法中,线程A调用join(2000)等待2s,如果线程A还没执行完,main停止等待,将和线程A一起并发执行。
yield() --线程让步
线程让步功能,调用yield()方法,不会阻塞该线程,他只是将线程转换成就绪状态,让系统重新调度一次。
public static native void yield();
* A hint to the scheduler that the current thread is willing to yield
* its current use of a processor. The scheduler is free to ignore this
* hint.
*
* <p> Yield is a heuristic attempt to improve relative progression
* between threads that would otherwise over-utilise a CPU. Its use
* should be combined with detailed profiling and benchmarking to
* ensure that it actually has the desired effect.
*
* <p> It is rarely appropriate to use this method. It may be useful
* for debugging or testing purposes, where it may help to reproduce
* bugs due to race conditions. It may also be useful when designing
* concurrency control constructs such as the ones in the
* {@link java.util.concurrent.locks} package.
*/
翻译一下
/**
*向调度程序提示当前线程愿意放弃它当前使用的处理器。调度程序可以随意忽略这一点提示。
*<p> yield是一种改进相对进步的启发式尝试,否则会过度使用CPU的线程之间。它的用途应结合详细的分析和基准测试确保它实际达到预期效果。
*使用这种方法很少合适。可能有用为了调试或测试的目的,可能有助于重现由于竞争条件而导致的错误。它在设计时也可能有用并发控制结构,如
* {@link java.util.concurrent.locks} package.
*/
总结:
(1)yield()声明在Thread类中的静态的本地native方法。
可能进入阻塞状态的情况:
1、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
2、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。如:当线程A运行过程中,试图获取同步锁时(抢占CPU),却被线程B获取;
3、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(sleep是不会释放持有的锁)。
线程终止方法(三种)
1、使用标志终止线程,在run方法中通过一个标记来进行结束;
2、使用interrupt终止线程
3、stop() 废弃了
Thread.stop, Thread.suspend, Thread.resume 和Runtime.runFinalizersOnExit 这些终止线程运行的方法已经被废弃。
1、使用标志终止线程
使用标志终止线程,在run方法中通过一个标记来进行结束;
public void run() {可参考
while(flag){
//do something
}
}
https://blog.csdn.net/jiadajing267/article/details/80137006
2、使用interrupt()终止线程 ---线程中断
中断(Interrupt)一个线程意味着在该线程完成任务之前停止其正在进行的一切,有效地中止其当前的操作。
public void interrupt() {}
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}private native boolean isInterrupted(boolean ClearInterrupted);
public boolean isInterrupted() {
return isInterrupted(false);
}
在java中主要有3个相关方法
在Thread类中 interrupt() 和 interrupted() 和 isInterrupted()
1、interrupt(),就是中断线程,将会设置该线程的中断状态位,即设置为true。中断的结果就取决于这个程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true)。它并不像stop方法那样会中断一个正在运行的线程。
使用说明:
(1)在一个线程A中调用另一个线程B的interrupt()方法(线程B被阻塞才会有用),只会改变线程B的中断状态,它不会中断一个正在运行的线程B。也就是给受阻塞的线程发出一个中断信号,这样受阻线程就得以退出阻塞的状态。
(2)如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出 InterruptedException。
(3)如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出InterruptedException中断异常(该线程必须事先预备好处理此异常InterruptedException)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。这一方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程检查到中断标识,就得以退出阻塞的状态。
2、interrupted() ,测试当前线程是否已经中断(Thread类,静态方法,返回boolean值)调用之后会改变线程的状态,如果是中断状态调用的,调用之后会清除线程的中断状态(也就是该方法调用后会将中断标示位清除,即重新设置为false)。
3、isInterrupted(),用来判断当前线程的中断状态(true or false)。
例
复制收展Javapackage com.test.mthread;
/**
* @Desc interrupt()线程中断-累行客
* @Author luolei
* @Web http://www.leixingke.com/
* @Date 2020/07/16 17:27
* 输出
* Thread1 Starting...
* Thread1 is running.
* Thread1 is running.
* Thread1 is running.
* Thread1isInterrupted(): false
* Thread1isInterrupted(): true
* throw InterruptedException...
* Thread1isInterrupted(): false
* java.lang.InterruptedException: sleep interrupted
* at java.lang.Thread.sleep(Native Method)
* at com.test.mthread.MyThread4.run(InterruptTest.java:54)
* Handling InterruptedException...
* Thread1 is exiting...
* Stopping application...
*/
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
MyThread4 m1 = new MyThread4("Thread1");
System.out.println(m1.getName() + " Starting...");
m1.start();
/*主线程main阻塞3s*/
Thread.sleep(3000);
System.out.println(m1.getName() + "isInterrupted(): " + m1.isInterrupted());/* false*/
/*阻塞时退出阻塞状态,即中断标示位设置为true*/
m1.interrupt();
System.out.println(m1.getName() + "isInterrupted(): " + m1.isInterrupted()); /* true*/
/*如果中断状态调用的,调用之后会清除线程的中断状态,即中断标示位设置为false*/
m1.interrupted();
System.out.println(m1.getName() + "isInterrupted(): " + m1.isInterrupted());/* false*/
Thread.sleep(3000); /*主线程main休眠3秒以便观察线程m1的中断情况*/
System.out.println("Stopping application...");
}
}
class MyThread4 extends Thread {
volatile boolean stop = false;
public MyThread4(String name) {
super(name);
}
public void run() {
while (!stop) {
System.out.println(getName() + " is running.");
try {
sleep(1000);
} catch (InterruptedException e) {
System.out.println("throw InterruptedException...");
e.printStackTrace();
System.out.println("Handling InterruptedException...");
stop = true; /*修改共享变量的状态,终止线程*/
}
}
System.out.println(getName() + " is exiting...");
}
}
- 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
3、使用stop()、destroy()等方法 ---废弃
stop():强制停止线程在以前的jdk版本中,使用stop方法中断线程,但是现在的jdk版本中已经不再推荐使用该方法了。
@Deprecated
public void destroy() {
throw new NoSuchMethodError();
}
destroy() 只是抛出了一个 NoSuchMethodError,所以该方法无法终止线程,该方法最初用于破坏该线程,但不作任何资源释放。它所保持的任何监视器都会保持锁定状态。不过该方法决不会被实现。
常见面试题
上述讲到的就不累述了。
(1)Thread 类中的start() 和 run() 方法有什么区别?
start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。
(2)为什么wait, notify 和 notifyAll没定义在thread类里面?
JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。
如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。
(3)为什么wait和notify方法只能在同步块中调用?
1. 只有在调用线程拥有某个对象的独占锁时,才能够调用该对象的wait(),notify()和notifyAll()方法。
2. 如果不在同步代码块中使用,会抛出IllegalMonitorStateException异常。
(4)synchronized 和 ReentrantLock 有什么不同?
说明:如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的。
两者区别:
1、这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要JVM实现。而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成。
2、由于ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能
(1)等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。
(2)公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁是非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。
(3)锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。
(5)对于 synchronized 关键字的了解
synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。
synchronized关键字的使用方式
修饰实例方法: 作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁。
修饰静态方法: 也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员(static 表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。
所以如果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。
修饰代码块: 指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁。synchronized 关键字加到实例方法上是给对象实例上锁。尽量不要使用 synchronized(String a) 因为JVM中,字符串常量池具有缓存功能。
(6)volatile关键字的作用?
当一个变量定义为 volatile 之后,将具备两种特性
1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。
2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。
(7)volatile和synchronized有什么不同?
(1)volatile本质是告诉JVM当前变量需要直接从主存中读取数据。
(2)synchronized是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
(3)volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。
(4)volatile仅能实现变量的修改可见性,并不能保证原子性;synchronized则可以保证变量的修改可见性和原子性。
(5)volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
(6)volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。
可参考 https://www.cnblogs.com/xichji/p/12446706.html
(8)你对线程池的理解
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行(已经常见好了,可直接使用)。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
待整理。。。