多线程
进程:进程指正在运行的程序。一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
线程线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
多线程:即一个程序中有多个线程在同时执行。
单线程程序:多个任务只能依次执行。当上一个任务执行结束后,下一个任务开始执行。
多线程程序:多个任务可以同时执行。
- 分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
- 抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
主线程
JVM启动后,必然有一个执行路径(线程)从main方法开始的,一直执行到main方法结束,这个线程在java中称之为主线程。
Thread类
- 开启多线程的方法之一:让类继承Thread类,并重写run()方法
- 重写run方法,run方法是Tread类定义的线程任务执行的位置
- 所以自己想要运行的代码必须写在run()中
public class exThread extends Thread {
public exThread(){
super("五花肉");//调用父类Thread类的重载构造方法设置线程名称
}
public void run() {
for(int i=0;i<1000;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
}
public static void main(String[] args) throws InterruptedException {//运行main时会开启一条主线程
exThread et=new exThread();//开启一条新线程"五花肉"
exThread et1=new exThread();//开启一条新线程
et.setName("邵猪");//设置/修改线程名称 "五花肉"->"邵猪"
et.start();
for(int i=0;i<1000;i++){
// Thread.sleep(1000);//进程休眠,放弃获取CPU的资源
System.out.println("main__"+i);
}
/*getName()为Thread的非静态方法,不可以在静态方法中直接调用*/
System.out.println(Thread.currentThread().getName());//获取main的线程名称
System.out.println(et.getName());//不修改线程名称时,默认Thread-0
System.out.println(et1.getName());//Thread-1
}
}
- 上述代码中有两条线程
- 线程执行的随机性:两个执行路径,CPU自己随机选择/分配时间,于是导致运行结果随机
- 分时调度,抢占调度
- 压栈遵行先进后出的规律,新线程会在内存中另开一个栈,于是才能出现方法运行时间的随机性
Runnable接口
- 开启多线程的方法之一:让类实现Runnable接口,并重写run()方法
- 重写run方法,run方法是新线程任务执行的位置
- 将Runnable接口实现类作为参数传递给Tread类对象
- 调用Thread类的start()方法
- 继承Runnable接口的方式可以避免(Thread类)单继承局限性
- 继承Runnable接口的方式将线程对象和线程任务实现了分离,低耦合
- 继承Thread类,一旦创建其子类对象,既是线程对象,又是线程任务,耦合性高
public class ImplementsRunnable implements Runnable {
public void run(){
System.out.println(Thread.currentThread().getName());
for(int i=0;i<1000;i++){
System.out.println(i);
}
}
}
public class RunnableDema {
/*
* 创建Thread对象,在构造方法中传递一个Runnable接口实现类
*/
public static void main(String[] args) {
ImplementsRunnable ir=new ImplementsRunnable();
Thread t=new Thread(ir);
t.start();
for(int i=0;i<1000;i++){
// Thread.sleep(1000);
System.out.println("main__"+i);
}
}
}
运行结果:(观察可得多线程同时进行)
Callable接口
Thread类的run()无返回值,如果程序需要有返回值,则使用Callable类中的call()可以设置返回值。
public class ImCallable implements Callable<Integer> {
private int a;
public ImCallable(int a){
this.a=a;
}
public Integer call(){
int sum=0;
for(int i=1;i<=a;i++){
sum=sum+i;
}
return sum;
}
}
线程池
创建线程和销毁线程消耗大,线程池相当于线程缓冲区,有利于节省资源和提高效率。
- Executors:使用线程池创建工厂类中的静态方法创建线程对象,指定线程的个数
public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
调用接口实现类对象中的方法submit提交线程任务,传递Runnable接口实现类对象 - ExecutorService:线程池类
Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行 - Future接口:用来记录线程任务执行完毕后产生的结果。
submit方法调用结束后,程序并不终止,因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中。
Callable接口线程池
public static void main(String[] args) throws Exception {
ExecutorService es = Executors.newFixedThreadPool(2);
//调用接口实现类对象es中的方法submit提交线程任务
//传递Runnable接口实现类对象
Future<Integer> f=es.submit(new ImCallable(100));
Future<Integer> f1=es.submit(new ImCallable(200));
System.out.println(f.get());
System.out.println(f1.get());
}
Thread类线程池
public static void main(String[] args) {
//调用工厂类的静态方法,创建线程池对象
//返回线程池对象,是返回的接口
ExecutorService es = Executors.newFixedThreadPool(2);
//调用接口实现类对象es中的方法submit提交线程任务
//传递Runnable接口实现类对象
es.submit(new ImplementsRunnable());
es.submit(new ImplementsRunnable());
es.submit(new ImplementsRunnable());
//es.shutdown();//销毁线程池;
}
线程有六种状态:新建、运行、阻塞、休眠、等待、停止