线程的生命周期(来源《JAVA并发编程的艺术》)
创建线程的方式
1、继承Thread类
public class ThreadTest extends Thread{
public void run(){}
}
public class Main {
public static void main(String[] args){
new ThreadTest().start();//一个线程调用两次start()方法将会抛出线程状态异常,也就是的start()只可以被调用一次
}
}
2、实现Runnable接口
覆写Runnable接口实现多线程可以避免单继承局限
public class ThreadTest implements Runnable {
public void run(){}
}
public class Main {
public static void main(String[] args){
ThreadTest t = new ThreadTest();
Thread thread = new Thread(t);
thread().start();
new Thread(new ThreadTest()).start();//或者这样写
}
}
3、Callable/Future(可以带返回值):
public class ThreadTest implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return ""SUCCESS"";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> callable =new ThreadTest();
FutureTask <String>futureTask=new FutureTask<>(callable);
Thread thread=new Thread(futureTask);
thread().start();
System.out.println(future.get()); //get()方法会阻塞,直到子线程执行结束才返回
}
}
4、使用线程池创建
Java通过Executors提供四种线程池:
(1)newCachedThreadPool:
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。不考虑硬件条件,可以有无限大的线程数进来。
public class ThreadTest implements Runnable {
@Override
public void run() {
try {
System.out.println(""Start"");
Thread.sleep(1000);
System.out.println(""线程标识:""+this.toString());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[]args) {
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
for(int i=0;i
结果:不考虑机器性能的情况下,可以有无限大的线程数进来。
(2) newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
public static void main(String[]args) {
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);//保持2个线程可以同时执行
for(int i=0;i
结果:
只能同时保持存在两个线程在执行。
(3) newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。
public static void main(String[]args) {
//这里是用 ScheduledExecutorService 创建
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2);
for(int i=0;i<4;i++) {
//这里是schedule()方法
newScheduledThreadPool.schedule(new ThreadTest(),3, TimeUnit.SECONDS);
}
}
结果:线程延迟3秒后才执行
-
schedule(commod,delay,unit) :系统启动后,需要等待delay的时间,然后开始执行。只执行一次,没有周期性。
-
scheduleAtFixedRate(commod,initialDelay,period,unit):以period为固定周期时间,按照一定频率来重复执行任务,initialDelay是说系统启动后,需要等待多久才开始执行。例如:如果设置了period为5秒,线程启动之后执行了大于5秒,线程结束之后,立即启动线程的下一次,如果线程启动之后只执行了3秒就结束了那执行下一次,需要等待2秒再执行。这个是优先保证任务执行的频率,
-
scheduleWithFixedDelay(commod,initialDelay,delay,unit):以delay为固定延迟时间,按照一定的等待时间来执行任务,initialDelay意义与上面的相同。例如:设置了delay为5秒,线程启动之后不管执行了多久,结束之后都需要先生5秒,才能执行下一次。这个是优先保证任务执行的间隔。
(4) newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
public static void main(String[]args) {
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
for(int i=0;i<4;i++) {
newSingleThreadExecutor.execute(new ThreadTest());
}
}
结果:
线程的中断(interrupt)
当一个线程while()、sleep()、join()、wait()时,如果想要中断它,不建议使用stop()方法来强制中断,因为程序无法知道这个线程执行到哪一步了,强行中断会产生问题。
正确的应该是用Interrupt来中断:
public class InterruptDemo02 implements Runnable{
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){ //false
try {
TimeUnit.SECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
//继续中断才能中断这个线程,否则在抛出异常后,线程会有一个复位,导致isInterrupted()重新回到false
Thread.currentThread().interrupt();
}
}
System.out.println(""End"");
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new InterruptDemo02());
t1.start();
Thread.sleep(1000);
t1.interrupt(); //使得isInterrupted()结果为true
}
}
join
如果一个线程实例A执行了threadB.join(),其含义是:当前线程A会等待threadB线程终止后threadA才会继续执行。
sleep
让当前线程按照指定的时间休眠,其休眠时间的精度取决于处理器的计时器和调度器。需要注意的是如果当前线程获得了锁,sleep方法并不会失去锁。
sleep() VS wait()
- sleep()方法是Thread的静态方法,而wait是Object实例方法
- wait()方法必须要在同步方法或者同步块中调用,也就是必须已经获得对象锁。而sleep()方法没有这个限制可以在任何地方种使用。另外,wait()方法会释放占有的对象锁,使得该线程进入等待池中,等待下一次获取资源。而sleep()方法只是会让出CPU并不会释放掉对象锁;
- sleep()方法在休眠时间达到后如果再次获得CPU时间片就会继续执行,而wait()方法必须等待Object.notift/Object.notifyAll通知后,才会离开等待池,并且再次获得CPU时间片才会继续执行。
yield
当前线程让出CPU,但是,需要注意的是,让出的CPU并不是代表当前线程不再运行了,如果在下一次竞争中,又获得了CPU时间片当前线程依然会继续运行。另外,让出的时间片只会分配给当前线程相同优先级的线程。
通过整型成员变量Priority来控制优先级,优先级的范围从1~10.在构建线程的时候可以通过setPriority(int)方法进行设置,默认优先级为5,优先级高的线程相较于优先级低的线程优先获得处理器时间片。
sleep()和yield()方法,同样都是当前线程会交出处理器资源,而它们不同的是,sleep()交出来的时间片其他线程都可以去竞争,也就是说都有机会获得当前线程让出的时间片。而yield()方法只允许与当前线程具有相同优先级的线程能够获得释放出来的CPU时间片。
参考:并发编程 Executors创建的4种线程池的使用 《java并发编程的艺术》