网络编程多线程(TCP 线程的创建) 1

 

线程与进程

  • 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
  • 线程:是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

进程与线程的区别:

  • 进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
  • 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。

1. 因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于CPU 的调度,程序员是干涉不了的。而这也就造成的多线程的随机性。

2. Java 程序的进程里面至少包含两个线程,主进程也就是 main()方法线程,另外一个是垃圾回收机制线程。每当使用 java 命令执行一个类时,实际上都会启动一个 JVM,每一个 JVM 实际上就是在操作系统中启动了一个线程,java 本身具备了垃圾的收集机制,所以在 Java 运行时至少会启动两个线程。

3. 由于创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建 多线程,而不是创建多进程。
 

线程的创建-继承方式

Java使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来创建启动多线程的步骤如下:

1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。2. 创建Thread子类的实例,即创建了线程对象3. 调用线程对象的start()方法来启动该线程

自定义线程类:

class MyThread  extends Thread{
    /*
    重写run方法,完成该线程执行的逻辑
     */

    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("自定义线程:" + i);
        }
    }
}

测试类:

public static void main(String[] args){
    //创建自定义线程对象
    MyThread mt = new MyThread();
    //开启新线程
    mt.statrt();
    //在主方法中执行for循环
    for(int i =0 ; i<200; i++){
        System.out.println("main主线程:"+i);
    }
}

线程的执行流程

程序启动运行 main 时候, java 虚拟机启动一个进程,主线程 main main()调用时候被创建。随着调用mt的对象的start方法,另外一个新的线程也启动了,这样,整个应用就在多线程下运行。通过这张图我们可以很清晰的看到多线程的执行流程,那么为什么可以完成并发执行呢?我们再来讲一讲原理。多线程执行时,到底在内存中是如何运行的呢?以上个程序为例,进行图解说明:多线程执行时,在栈内存中,其实 每一个执行线程都有一片自己所属的栈内存空间 。进行方法的压栈和弹栈。

线程内存图

当执行线程任务结束了,线程自动在栈内存中释放了,但是当所有执行线程都结束了,那么进程就结束了。

 run()方法和start()方法

  • run()方法,是线程执行的任务方法,每个线程都会调用run()方法执行,我们将线程要执行的任务代码都写在run()方法中就可以被线程调用执行。
  • start()方法,开启线程,线程调用run()方法。start()方法源代码中会调用本地方法start0()来启动线程:private native void start0(),本地方法都是和操作系统交互的,因此可以看出每次开启一个线程的线程都会和操作系统进行交互。
  • 注意:一个线程只能被启动一次!!!

线程名字的设置和获取

  • Thread类的方法String getName(String name)设置线程的名字
  • 通过Thread类的构造方法Thread(String name)也可以设置线程的名字
public class MyThread extends Thread{
    public void run(){
        System.out.println("线程名字:" +super.getName());
    }
}

测试类:

public class Demo{
    public static void main(String[] args){
        //创建自定义线程对象
        MyThread mt = new MyThread();
        //设置线程名字
        mt.setName("旺财");
        //开启新线程
        mt.start();
    }
}

注意:线程是有默认名字的,如果我们不设置线程的名字,JVM会赋予线程默认名字Thread-0,thread-1....

获取运行main方法线程的名字

  • Demo类不是Thread的子类,因此不能使用getName()方法获取。
  • Thread类定义了静态方法static Thread currentThread()获取到当前正在执行的线程对象
  • main方法也是被线程调用了,也是具有线程名字的。
public static void main(String[] args){
    Thread t = Thread.currentThread();
    System.out.println(t.getName());
}
public static void main(String[] args){
    Thread t = Thread.currentThread();
    System.out.println(t.getName());
}

线程的创建-实现方式

实现Runnable接口方式

采用java.lang.Runnable也是非常常见的一种,我们只需要重写run方法即可。

步骤如下:

1. 定义 Runnable 接口的实现类,并重写该接口的 run() 方法,该 run() 方法的方法体同样是该线程的线程执行体。
2. 创建 Runnable 实现类的实例,并以此实例作为 Thread target 来创建 Thread 对象,该 Thread对象才是真正的线程对象。
3. 调用线程对象的 start() 方法来启动线程。
public class MyRunnable implements Runnable{
    public void run(){
        for(int i=0;i<20;i++){
            System.out.println(Thread.currentThread().getName()+" " +i);
        }
   }
}
public class Demo{
   public static void main(String[] args){
        //创建自定义类对象,线程任务对象
        Runnable mr = new MyRunnable();
        //创建线程对象
        Thread t = new Thread(mr);
        t.start();
        for(int i = 0;i<20;i++){
            System.out.println("main:"+i);
    }
}
通过实现 Runnable 接口,使得该类有了多线程类的特征。 run()方法是多线程程序的一个执行目标。所有的多线程代码都在 run 方法里面。 Thread 类实际上也是实现了 Runnable 接口的类。
在启动的多线程的时候,需要先通过 Thread 类的构造方法 Thread(Runnable target) 构造出对象,然后调用Thread对象的 start() 方法来运行多线程代码。
实际上所有的多线程代码都是通过运行 Thread start() 方法来运行的。因此,不管是继承 Thread 类还是实现Runnable接口来实现多线程,最终还是通过 Thread 的对象的 API 来控制线程的,熟悉 Thread 类的 API是进行多线程编程的基础。

Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

实现Runnable接口比继承Thread类所具有的优势:

1. 适合多个相同的程序代码的线程去共享同一个资源。
2. 可以避免 java 中的单继承的局限性。
3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
 

 匿名内部类方式创建线程

使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法:

public class NoNameInnerClassThread{
    public static void main(String[] args){
        //new Runnable(){
        //    public void run(){
        //        for(int i = 0;i<20;i++){
        //            System.out.println("wensong:"+i);
        //        }
        //    }
        //};//这个整体相当于new MyRunnable()
        Runnable r = new Runnable(){
            public void run(){
                for(int i=0;i<20;i++){

                   System.out.println("wensong:"+i);
                }
            }
        };
        new Thread(r).start();
        for (int i=0;i<20;i++){
            System.out.println("qing:"+i);
        }
    }
}             
       

猜你喜欢

转载自blog.csdn.net/weixin_40959890/article/details/107396578