多线程
Posted createtable
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。
1 线程与进程:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。多线程即一个程序中有多个线程在同时执行。
2 多线程调度模式:
分时调度:所有线程轮流使用CPU,平均分配每个线程占用CPU的时间。
抢占式调度:优先级高的线程优先使用CPU,优先级相同,随机选择一个(线程随机性),Java使用的为抢占式调度。
抢占式调度在多个线程间高速切换。对于CPU的一个核而言,某个时刻只能执行一个线程,而 CPU在多个线程间切换速度很快,感觉像是在同一时刻运行。多线程并不能提高 运行速度,但能提高运行效率,让CPU的使用率更高。
3 主线程:JVM启动后,主线程从main方法开始,一直到main方法结束。
4 创建新线程的方法:
①定义一个类继承Thread类,该子类重写Thread类的 run方法。创建子类对象,即创建线程对象,调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。
1 //自定义测试类 2 public class Demo01 { 3 public static void main(String[] args) { 4 //创建自定义线程对象 5 MyThread mt = new MyThread(); 6 //开启新线程 7 mt.start();//线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。调用run方法不开启线程,只是调用对象的方法。 8 //在主方法中执行for循环 9 for (int i = 0; i < 10; i++) { 10 System.out.println("main线程!"+i); 11 } 12 } 13 } 14 //自定义线程类 15 public class MyThread extends Thread { 16 //定义指定线程名称的构造方法 17 public MyThread(String name) { 18 //调用父类的String参数的构造方法,指定线程的名称 19 super(name); 20 } 21 /** 22 * 重写run方法,完成该线程执行的逻辑 23 */ 24 @Override 25 public void run() { 26 for (int i = 0; i < 10; i++) { 27 System.out.println(getName()+":正在执行!"+i); 28 } 29 } 30 }
为什么要继承Thread类,并调用其start方法才能开启线程呢?
继承Thread类:因为Thread类用来描述线程,具备线程应该有功能。直接mt.run();是直接调用了MyThread对象的run方法,重写的run方法执行了,但没有开启多线程。
那为什么不直接创建Thread类的对象呢?如下代码:
Thread t1 = new Thread();
t1.start();
这样做没有错,但是该start调用的是Thread类中的run方法而不是我们创建的MyThread类中的run方法,这个run方法并没有我们需要让线程执行的代码。
②声明一个类,实现Runnable接口。该类实现run方法。创建Runnable的子类对象,new一个新Thread对象,将Runnable子类对象传入到新Thread的构造方法中,开启线程。
1 //测试类 2 public class Demo02 { 3 public static void main(String[] args) { 4 //创建线程执行目标类对象 5 Runnable runn = new MyRunnable(); 6 //将Runnable接口的子类对象作为参数传递给Thread类的构造函数 7 Thread thread = new Thread(runn); 8 Thread thread2 = new Thread(runn); 9 //开启线程 10 thread.start(); 11 thread2.start(); 12 for (int i = 0; i < 10; i++) { 13 System.out.println("main线程:正在执行!"+i); 14 } 15 } 16 } 17 //自定义线程执行任务类 18 public class MyRunnable implements Runnable{ 19 20 //定义线程要执行的run方法逻辑 21 @Override 22 public void run() { 24 for (int i = 0; i < 10; i++) { 25 System.out.println("我的线程:正在执行!"+i); 26 } 27 } 28 }
实现Runnable的好处:
第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。
5 多线程内存分配:每创建一个新线程都会开辟一个新的栈内存空间,线程任务结束释放栈内存。
6 获取线程的名称:Thread.currentThread().getName();(原来主线程的名称:main;自定义的线程:Thread-0,多个线程时,数字顺延。如Thread-1......)
7 多线程设置名称:测试类中mt.setName("xxx");或者MyThread类中super("xxx");调用父类构造器。一般情况不需要设置名称。
8 多线程内使用匿名内部类:
1 //方式1:创建线程对象时,直接重写Thread类中的run方法 2 new Thread() { 3 public void run() { 4 for (int x = 0; x < 40; x++) { 5 System.out.println(Thread.currentThread().getName() + "...X...." + x); 6 } 7 } 8 }.start(); 9 10 //方式2:使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法 11 Runnable r = new Runnable() { 12 public void run() { 13 for (int x = 0; x < 40; x++) { 14 System.out.println(Thread.currentThread().getName() + "...Y...." + x); 15 } 16 } 17 }; 18 new Thread(r).start();
9 线程池及作用:线程池,容纳多个线程的容器,其中的线程可以反复使用,省去了创建线程对象的操作,无需反复创建线程而消耗过多资源。
在java中,创建/销毁线程消耗的时间和系统资源都很大,甚至可能要比在处理用户请求的时间和资源要多。运行中的线程也占用系统资源,如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。
10 使用线程池的方式:
①Runnable接口
Executors:线程池创建工厂类
public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
ExecutorService:线程池类
Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
Future接口:用来记录线程任务执行完毕后产生的结果。
使用线程池中线程对象的步骤:
1创建线程池对象
2创建Runnable接口子类对象
3提交Runnable接口子类对象
4关闭线程池
1 //测试类 2 public class ThreadPoolDemo { 3 public static void main(String[] args) { 4 //1创建线程池对象 5 ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象 6 //2创建Runnable子类对象 7 MyRunnable r = new MyRunnable(); 8 9 //自己创建线程对象的方式 10 //Thread t = new Thread(r); 11 //t.start(); ---> 调用MyRunnable中的run() 12 13 //3提交Runnable接口子类对象 14 service.submit(r); 15 //再获取个线程对象,调用MyRunnable中的run() 16 service.submit(r); 17 service.submit(r); 18 //注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中 19 20 //关闭线程池 21 //service.shutdown(); 22 } 23 } 24 //Runnable接口实现类:略
②Callable接口
Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call 方法可抛出异常。
ExecutorService:线程池类
<T> Future<T> submit(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的call()方法
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
使用线程池中线程对象的步骤:
1创建线程池对象
2创建Callable接口子类对象
3提交Callable接口子类对象
4关闭线程池
以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章