多线程

1.什么是进程?什么是线程?

  进程是一个应用程序(一个进程是一个软件)

  线程是一个进程中的执行场景/执行单元

  一个进程可以启动多个线程

2.对于java程序来说,当在DOS命令窗口中输入:java Helloworld 回车之后。会先启动JVM,而JVM就是一个进程。JVM再启动一个主线程调用main方法,同时再启动一个垃圾回收线程负责看护,回收垃圾。最起码,现在的java程序中,至少有两个线程并发,一个是垃圾回收线程,一个是执行main方法的主线程。

 

3.进程和线程是什么关系?

  进程可以看作是一个公司,线程可以看作是进程中的某个员工。

  注:进程A和进程B的内存独立不共享,可以理解为两个公司的资源不能共享

  线程A和线程B:只要是一个进程里的,堆内存方法区内存共享;但是栈内存独立,一个线程一个栈。 

 

   假设启动10个线程,会有10个栈空间,每个栈和每个栈之间互不干扰,各自执行各自的,这就是多线程并发。

  火车站可以看作一个进程,火车站中每一个售票窗口可以看作是一个线程,A乘客可在窗口1购票,B乘客可在窗口2购票,A不需要等B,所以多线程并发可以提高效率。

  java中之所以有多线程机制,目的就是为了提高程序的处理效率

4.使用多线程机制之后,main方法结束后,程序可能也不会结束,main方法结束只是主线程结束了,主栈空了,但是其他的栈(线程)可能还在压栈弹

 

 

 5.对于单核的CPU来说,可以做到真正的多线程并发吗?(对于多核CPU来说,真正的线程并发是没问题的,4核CPU表示同一个时间点上,可以真正的有4个进程并发执行)

  什么是真正的多线程并发?

    t1线程执行t1的,t2线程执行t2的,两者相互不影响,这叫做真正的多线程并发。

  单核CPU表示只有一个大脑,不能做到真正的多线程并发,在某个时间点上只能处理一件事情,但是由于人的反应速度比较慢,而CPU的处理速度极快,多个线程之间频繁快速切换执行,给人造成的感觉就多线程并发,即多个事情同时在做。

  例如:线程A播放音乐,线程B运行游戏,单核CPU在一个时间点只能运行一个线程,但是由于A与B之间来回切换的速度极快,所以给人的感觉就是音乐一直在播放,音乐一直在运行,即它们是同时并发的。

6.线程分析

 1 package XianChengFenXi;
 2 
 3 
 4 //分析程序有几个线程
 5 //除垃圾回收之外有几个线程
 6 
 7 
 8 //因为程序只有一个栈,所以除了垃圾回收之外,只有一个线程
 9 public class ThreadTest1 {
10     public static void main(String[] args){
11         System.out.println("main begin");
12         m1();
13         System.out.println("main over");
14     }
15     
16     private static void m1(){
17         System.out.println("m1 begin");
18         m2();
19         System.out.println("m1 over");
20     }
21     
22     private static void m2(){
23         System.out.println("m2 begin");
24         m3();
25         System.out.println("m2 over");
26     }
27     
28     private static void m3(){
29         System.out.println("m3 execute");
30     }
31 
32 }

 

 6.java语言中。实现线程有两种方式:

  java支持多线程机制,并且java已经将多线程实现了,只需要继承就行。

  (1)第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法

 1 package XianChengFenXi;
 2
11 public class ThreadTest02 {
12     public static void main(String[] args){
13         //这里是main方法,这里的代码属于主线程,在主栈中运行。
14         
15         //创建一个分支线程对象
16         MyThread myThread=new MyThread();
17         
18         myThread.run();  //若直接调用run()方法,而不事先启动start()方法,则不会分配新的分支栈,还是在主栈中进行,为单线程。
28         for(int i=0;i<10;i++){
29             System.out.println("主线程++++++++"+i);
30         }
31         
32     }
33 
34 }
35 class MyThread extends Thread{
36     public void run(){
37         //编写程序,这段程序运行在分支栈
38         for(int i=0;i<10;i++){
39             System.out.println("分支线程——————————"+i);
40         }
41         
42     }
43 }

运行结果:可以看到,这就是简单的主栈中的方法调用,并没有进行线程并发。

 

 ---------------------------------------------------------------------------------------------------------------------------------

 1 package XianChengFenXi;
 2 
 3 
 4 /*
 5  * 实现线程的第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法
 6  *  
 7  *  如何创建线程对象?          new就行了
 8  *  怎么启动线程?                 调用线程对象的start()方法
 9  */
10 
11 public class ThreadTest02 {
12     public static void main(String[] args){
13         //这里是main方法,这里的代码属于主线程,在主栈中运行。
14         
15         //创建一个分支线程对象
16         MyThread myThread=new MyThread();19         
20         //启动线程
21         //start()方法的作用是,启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成后,瞬间就结束了
22         //这段代码的任务只是为了开辟一个新的栈空间,只要新的栈空间开出来,start()方法就结束了,线程就启动成功了
23         //启动成功的线程会自动调用run方法,并且run方法在分支栈的底部(压栈)。
24         //run()方法在分支栈的栈底部,main方法在主栈的栈底部,run和main是平级的
25         myT hread.start();
26         
27         //这里的代码还是运行在主线程中
28         for(int i=0;i<10;i++){
29             System.out.println("主线程++++++++"+i);
30         }
31         
32     }
33 
34 }
35 class MyThread extends Thread{
36     public void run(){
37         //编写程序,这段程序运行在分支栈
38         for(int i=0;i<10;i++){
39             System.out.println("分支线程——————————"+i);
40         }
41         
42     }
43 }

运行结果:多线程并发

 

注:结果中出现主线程、分支线程交替输出,有多有少,有先有后,原因是:因为控制台只有一个,存在先执行后打印的情况

  (2)第二种方式:编写一个类,实现java.lang.Runnable接口,实现run()方法

package XianChengFenXi;

/*
实现线程的第二种方式:编写一个类实现java.lang.Runnable接口
*/
public class ThreadTest3 {
    
    public static void main(String[] args){
        
        //创建一个可运行的对象
        MyRunnable r=new MyRunnable();
        
        //将可运行的对象封装成一个线程对象
        Thread t=new Thread(r);
        
        //启动线程
        t.start();
        for(int i=0;i<10;i++){
            System.out.println("主线程++++++++"+i);
        }
        
    }

}

//这并不是一个线程类,是一个可运行的类,它还不是一个线程
class MyRunnable implements Runnable {
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println("分支线程——————————"+i);
        }
    }
}

注意:第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承其它的类,更灵活

-------------------------------------------------------------------------------------------------------------------------------------

 

  (3第三种方式:采用匿名内部类

 1 package XianChengFenXi;
 2 
 3 
 4 //采用匿名内部类
 5 public class ThreadTest4 {
 6     public static void main(String[] args){
 7         //创建线程对象,采用匿名内部类方式
 8         //这是通过一个没有名字的类,new出来的对象
 9         Thread t=new Thread(new Runnable(){
10             public void run(){
11                 for(int i=0;i<10;i++){
12                     System.out.println("分支线程------"+i);
13                 }
14             }
15         });
16         
17         /*
18          * 与上面的代码功能一样,只是拆开了写
19          Runnable r=new Runnable(){
20             public void run(){
21                 for(int i=0;i<10;i++){
22                     System.out.println(i);
23                 }
24             }
25         };
26          
27          Thread t=new Thread(r);
28          
29          
30          */
31         
32         //启动线程
33         t.start();
34         for(int i=0;i<10;i++){
35             System.out.println("主线程+++++++"+i);
36         }
37         
38     }
39 
40 }

 

posted @ 2020-10-12 21:44  L1998  阅读(133)  评论(0)    收藏  举报