多线程

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关闭线程池

  























以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章

线程学习知识点总结

多个请求是多线程吗

python小白学习记录 多线程爬取ts片段

多线程编程

多线程编程

python多线程