一、程序、进程和线程
- 程序:为了完成一个任务,用某种语言编写的指令集和,即一段静态代码。
- 进程:是在工作运行中的程序。进程是有生命周期的;进程作为资源分配的单元,系统在运行时为每个进程分配不同的内存区域。
- 线程:进程可以进一步划分为线程,是一个程序的内部路径。若一个进程同一时间并行指向多个线程,就是支持多线程;线程作为调度和执行的单位,每个线程都拥有独立的运行栈和程序计数器;一个进程中的多个线程共享相同的内存地址空间,它们从同一个堆中分配对象,可以访问相同的变量及对象。
一般来说java应用程序至少有三个线程:main()主线程,gc()垃圾回收线程以及异常处理线程。
二、并行、并发、多线程优点
- 并行:多个CPU同时执行多个任务。
- 并发:一个CPU同时执行多个任务(相当于在任务之间快速切换)。
- 多线程优点:
可以提高应用程序的响应;
提高CPU利用率;
改善程序结构。 - 何时需要多线程:
程序需同时执行多个任务;
程序需要实现一些需要等待的任务,如用户输入、文件读写操作、网络操作、搜索等。
需要一些后台运行程序。
三、多线程的创建方式
1. 继承thread类,重写run()方法:
//多线程创建方法一:
// 1.创建一个继承Thread类的子类
class MyThread extends Thread{
//2.重写run方法
@Override
public void run() {
for(int i = 0; i < 100 ;i++){
//打印当前线程名称
System.out.println(this.getName() + ":" +i);
}
}
}
public class TreadTest {
public static void main(String[] args) {
//3.创建子类对象并调用start()方法开启线程
MyThread t1 = new MyThread();
t1.start();//开启第一个线程
MyThread t2 = new MyThread();
t2.start();//开启第二个线程
System.out.println("hello");//主线程
}
}
结果如下图所示:其结果并不唯一,线程一和线程二。
继承thread()类有个缺点:无法再继承其它类。因此有了第二种线程创建方法实现Runnable()接口。
2.实现Runnable()接口,重写run()方法:
//创建方式二:实现Runnable()接口
class myThread implements Runnable{
@Override
public void run() {
for(int i = 0;i < 50;i++){
//不能使用this.getName()得到当前线程了
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class TreadTest1 {
public static void main(String[] args) {
//创建线程对象,把实现了Runnable接口的对象作为参数传入;
myRunnable m1 = new myRunnable();
Thread t1 = new Thread(m1);
myRunnable m2 = new myRunnable();
Thread t2 = new Thread(m2);
//启动两个线程
t1.start();
t2.start();
System.out.println("hello");
}
}
执行结果如下,与第一种方式实现效果是一样的,线程一和线程二抢占式执行。
由于 前两种创建方式都无返回值,因此有了第三种线程创建方式。
3.实现callable接口,重写call()方法:
//创建多线程方法三:继承Callable方法
class MyCallable implements Callable{
//重写call()方法
@Override
public Object call() throws Exception {
for(int i = 0;i < 50;i++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
return 0;
}
}
public class ThreadTest3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建callable对象
MyCallable c1 = new MyCallable();
MyCallable c2 = new MyCallable();
//创建执行任务
ExecutorService s = Executors.newFixedThreadPool(2);
//提交执行任务得到future对象
Future<Integer> f1 = s.submit(c1);
Future<Integer> f2 = s.submit(c2);
//得到返回结果
int num1 = f1.get();
int num2 = f2.get();
}
}
运行结果如下:
四、线程创建方法比较
- 继承Thread类编程比较简单,可以通过this.的方式获取该线程名儿,且多个线程可以使用同一份数据资源,但是继承该Thread类后就无法继承其他类。
- 实现Runnable()接口和Callable()接口编程比较复杂,访问当前线程名需调用Thread.currentThread()方法,适合多个线程处理同一份资源,Callable()比Runnable()多了一个返回值的功能。