进程:进程是资源分配的最小单位;进程=独立的代码+数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1-n个线程;
线程:线程是CPU调度的最小单位;线程=堆栈+程序计数器+同一类线程共享的代码和数据空间,线程的切换开销较小;
线程组:线程可以分组,Java默认创建的线程都属于系统线程组;作用是:出于安全考虑;因为同一个线程组的线程可以相互修改对方的数据;如果不是同一个线程组中,则不能修改,所以可以保证数据安全;
线程池:专门用来存放很多线程的池子;作用是:出于效率考虑;由于线程的创建和结束都需要消耗系统资源,频繁的创建肯定是降低性能的。因此将线程放在线程池中,使得线程重复使用,可以省去线程的反复创建与销毁,从而节省时间,提高效率;
【线程池和线程组共同点】
1、都是管理一定数量的线程;
2、都可以对线程进行控制——休眠、唤醒、结束、创建、中断、暂停,但不一定包含全部这些操作。
设置线程任务的方式:Thread 、Runnable 、Callable (run/call方法)
创建并启动线程的方式:Thread的start();线程池的submit()方法
【线程的创建】
Thread
1、通过继承Thread类的方式,重写run方法,则设置了线程的任务;run()方法是线程的任务;
class MyThread extends Thread{}
创建线程并启动的方式:new MyThread().start();
2、通过实现Runnable接口的方式,实现run方法,则设置了线程的任务;run()方法是线程的任务;
class MyRunnable implements Runnable{
@Override
void run(){}
}
创建线程并启动的方式:new Thread(new MyRunnable()).start();
3、通过实现Callable接口的方式,实现call方法,则设置了线程的任务;call()方法是线程的任务;
class MyCallable implements Callable{
@Override
call(){ return T;}
}
创建线程并启动的方式:
CallablemyCallable = new MyCallable();
FutureTaskfutureTask = new FutureTask(myCallable);
new Thread(futureTask).start();
try{
futureTask.get();
}catch(Exception e){
e.printStackTrace();
}
Thread和Runnable的异同:
1、Thread是一个类,需要继承;Runnable是一个接口,需要实现;由于Java是单继承,所以推荐Runnable方式创建线程;
2、Thread类中的start方法启动线程后,事实上在运行时执行的是Runnable的run方法(Thread的源码中有一个Runnable target的属性,当我们调用thread的start方法时,事实就是运行时执行了target的run方法;start方法和run方法耦合在一起);而Runnable在启动线程时,是先new一个Runnable类的实例,在包装成一个Thread的实例,在执行start方法。就好像把线程的创建和具体的业务做了解耦,代码和数据独立。多线程下可以共享资源;
如:Runnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
示例中,创建了两个线程,最终使用了同一个Runnable实例。如果Runnable实例中包含了一个类变量,那么两个线程对这个变量操作就相互影响,这个变量就是共享的;
3、线程池只能放入Runable或Callable类线程,不能直接放入继承Thread的类。
Runnable相比Thread的优势:
1、适合多个相同的程序代码的线程去共享一个资源;
2、避免Java中单继承的局限性;
3、增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。(实现Runnable接口的方式,把设置线程任务和开启线程进行了分离(解耦),实现类中重写了run方法来设置线程任务,而创建Thread类对象调用start方法只是用来开启新的线程)
4、线程池只能放入Runable或Callable类线程,不能直接放入继承Thread的类。
Runnable和Callable的异同:
1、两个都是接口,需要实现;
2、Runnable的run方法没有返回值,也不能抛出异常;Callable的call方法有返回值,还支持通过泛型规定call的返回值,同时还会抛出异常;
3、Future是对Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作。
4、Callable接口带来了灵活的线程使用体验,调用FutureTask的get方法,从而获取call方法的返回值;FutureTask的源码显示,它实现了RunnableFuture接口,而RunnableFuture又继承了Runnable和Future接口。这也解释了为什么FutureTask能够作为Thread构造方法的一个参数,同时又能够通过get方法取得返回值;