多线程
Java给多线程编程提供了内置的支持。一个多线程程序包含两个或多个能并发运行的部分。程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行内容。
多线程是多任务的一种特别的形式。多线程比多任务需要更小的开销。
进程: 一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
线程 : 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位;
多线程能满足程序员编写非常有效率的程序来达到充分利用CPU的目的,因为CPU的空闲时间能够保持在最低限度。
线程的生命周期
线程共包括以下 5 种状态:
- 新建状态(New): 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
- 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
- 运行状态(Running): 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
- 阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
- (01) 等待阻塞 – 通过调用线程的wait()方法,让线程等待某工作的完成。
- (02) 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
- (03) 其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
- 死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程优先级
在java中,线程的优先级的范围是MIN_PRIORITY(1)和MAX_PRIORITY(10),默认的优先级是NORM_PRIORITY(5);优先级高的不一定会比优先级低的先执行。
如何创建一个线程?
1.继承Thread类
定义Thread类的子类,并重写该类的 run() 方法,该 run() 方法的方法体就代表了线程需要完成的任务。
public class HelloThread extends Thread{
@Override
public void run() {
System.out.println("继承Thread类--创建线程");
}
public static void main(String[] args) {
HelloThread ht = new HelloThread();
ht.start();
}
}
2.实现Runnable接口
定义Runable接口的实现类,并且重写该接口的run() 方法,该run() 方法的方法体就是线程的线程执行体
创建Runable实现类的实例,并且以此实例作为Thread类的target来创建Thread对象,该Thread对象才是真正的线程对象。
public class HelloRunable implements Runnable{
@Override
public void run() {
System.out.printf("实现Runable 接口创建线程");
}
public static void main(String[] args) {
HelloRunable helloRunable = new HelloRunable();
Thread thread = new Thread(helloRunable);
thread.start();
}
}
3.Callable接口
使用Callable创建线程和Runnable接口方式创建线程比较相似,不同的是,Callable接口提供了一个call() 方法作为线程执行体,而Runnable接口提供的是run()方法,同时,call()方法可以有返回值,而且需要用FutureTask类来包装Callable对象。
public class MyCallable implements Callable {
@Override
public Integer call() {
int sum = 0;
for (int i =0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
sum += i;
}
return sum;
}
}
public class ThreadTest {
public static void main(String[] args) {
Callable<Integer> myCallable = new MyCallable(); // 创建MyCallable对象
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask来包装MyCallable对象
Thread thread = new Thread(ft); //FutureTask对象作为Thread对象的target创建新的线程
thread.start(); //线程进入到就绪状态
try {
int sum = ft.get(); //取得返回的结果
System.out.println("sum = " + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
总结:
实现Runnable接口相对于继承Thread类来说,有如下的显著优势:
1 .适合多个相同代码的线程去处理同一个资源的情况
2.可以避免由于java的单继承特性带来的局限
3.增强了程序的健壮性,代码能够被多个线程共享,代码与数据时独立的
当需要线程执行完有返回结果,我们需要选择Callable创建线程。