多线程的意义:
线程是依托于进程而存在的,至于线程与进程的区别在于,进程属于资源分配的单位,而线程则是作业调度的单位;进程拥有自己的地址空间,而多个线程拥有自己的堆栈和局部变量,并共享所依托于进程的资源。多进程操作的意义在于多个进程轮流共享CPU的时间片(针对单处理器情形,多处理器可理解为并行),属于并发操作。多线程的并发操作优势在于当程序遇到阻塞的情形。
当程序的某个任务因外部条件问题,导致阻塞,如果没有并发,则整个程序停止,直到外部条件发生变化,使用并发后,程序中的其他任务还可以继续执行,如果没有阻塞,在单处理器上考虑多线程并发也就没有意义了,线程之间的切换会造成大量的时间消耗。
Java中三种多线程的启动方式
- 继承Thread类,重写run方法
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}
}
- 实现Runable接口,重写run方法
public class HelloThread extends Thread {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new HelloThread()).start();
}
}
关于的不同在于Java语言本身只支持单继承的局限性,利用Runable接口,当子类已经继承与其他类,仍可以实现Runable接口进行多线程操作。然而,从Runable接口和Thread类的源码角度分析,Thread类实现了Runable接口,并且定义了更多用户线程管理的方法,因此,可控性和功能会更丰富一点。
无论采用哪种方式,都必须将实例传给new Thread()对象,然后调用start()的方法,使线程进入就绪态,等待CPU空闲才可执行。
参考文档
- 利用Callable和Future
对于上面两种实现多线程的方式,父线程均无法获取子线程的返回值,针对这一问题,我们下面介绍第三种实现多线程的方式,可以从子线程中获取返回值。
class CallableThread implements Callable<String>{
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return Thread.currentThread().getName();
}
}
ExecutorService executor = Executors.newFixedThreadPool(10);
List<Future<String>> list = new ArrayList<Future<String>>();
Callable<String> callable = new CallableThread();
for(int i =0;i<100;i++){
//使用submit方法调用call方法,并且返回Future对象
Future<String> future = executor.submit(callable);
list.add(future);
}
for (Future<String> future : list) {
try {
//get方法会阻塞,直到准备结果就绪,在调用get方法前,可以先用isDone检测是否执行完毕。
System.out.println(new Date()+"::"+future.get());
} catch (InterruptedException | ExecutionException e) {
// TODO: handle exception
e.printStackTrace();
}
}
executor.shutdown();
Callable是一个具有类型参数的泛型,表示call方法的返回值,也就是函数执行体(类似于run),并返回一个Future对象,而且,call方法要通过ExecutorService.submit调用。
这里,可以想想,这个机制是否和回调有点相似。
以上就是Java多线程的三种实现方式,仅此作为自己的学习记录。