线程概念
线程是执行任务的路径,也是分配CPU时间的最小单位,也可以说线程是独立调度和分派的基本单位。
线程与进程区别
- 程序运行起来就是进程,一个程序包含一个或多个进程,需要内存和CPU资源进行运算执行,每个进程有自己独立的内存空间,它是重量级的,需要的系统资源比较多。
- 一个进程包含多个线程,多个线程共享一个进程的内存空间,每个线程单独执行一个任务,它是轻量级的,需要的系统资源比较少。
线程的实现
一、继承Thread类
- 继承Thread类
- 重写run方法
- 创建线程对象,调用start方法
public class MyThread extends Thread{
/**
* 执行线程任务的方法
*/
public void run(){
//Thread.currentThread()是获得系统当前执行的线程
System.out.println(Thread.currentThread().getName()+"线程执行了!!!");
}
public static void main(String[] args) {
//主线程中执行
System.out.println(Thread.currentThread().getName()+"线程执行了!!!");
//创建线程对象
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
//启动线程
thread1.start();
thread2.start();
}
}
这里有两个问题在面试时有可能会问到
启动线程使用start()和run()有什么区别?
启动线程要使用start()方法,只有start()方法会去创建并执行线程,运行任务,而run()方法里面写的只是我们要运行的任务,它不会启动线程。
线程执行为什么没有规律?
因为线程的执行是抢占式,它们去抢CPU,哪个线程抢到了CPU,哪个线程才执行。
二、实现Runnable接口
- 实现Runnable接口
- 实现run方法
- 创建自定义线程对象,作为参数传入Thread对象
- 调用start方法
public class MyRunnable implements Runnable{
/**
* 实现run方法
*/
@Override
public void run() {
System.out.println("当前执行的线程是:"+Thread.currentThread().getName());
}
public static void main(String[] args) {
//创建Thread对象,传入Runnable对象
Thread thread1 = new Thread(new MyRunnable());
//调用start方法
thread1.start();
//使用匿名内部类实现Runnable
Thread thread2 = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("匿名内部类,当前执行的线程是:"+Thread.currentThread().getName());
}
});
thread2.start();
//使用Lambda实现Runnable
Thread thread3 = new Thread(()->{
System.out.println("Lambda,当前执行的线程是:"+Thread.currentThread().getName());
});
thread3.start();
}
}
那么这两种方式有什么区别呢?
- 实现Runnable接口还可以继续继承或者实现其他的接口或者类,但是一旦继承了Thread类,因为Java是单继承的,所以就不能继承其他的类了,会对后续的开发造成影响。
- 实现Runnable接口就必须实现run()方法,而继承Thread类不用必须实现run()方法
三、实现Callable接口
实现Callable接口与上述两种方式的区别是,Callable接口可以返回值,但是上述两种方式实现的线程没有返回值
- 实现Callable接口,实现call方法
- 创建FutureTask对象,传入Callable对象
- 创建Thread对象,传入FutureTask对象
- 调用Thread对象的start方法
- 调用FutureTask对象的get方法,获得返回值
public class MyCallable implements Callable<Long>{
@Override
public Long call() throws Exception {
System.out.println("执行call的线程是:"+Thread.currentThread().getName());
//模拟执行耗时运算
Long sum = 0L;
for(int i = 0;i < 100000000L;i++){
sum += i;
}
//返回结果
return sum;
}
public static void main(String[] args) {
//创建FutureTask对象,传入Callable对象
FutureTask<Long> task = new FutureTask<>(new MyCallable());
//创建Thread,传入FutureTask对象
Thread thread = new Thread(task);
//启动线程
thread.start();
//获得结果
try {
// long result = task.get();
long result = task.get(5,TimeUnit.SECONDS);
System.out.println("获得计算结果:"+result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}