多线程概述和两种实现方式

1.进程和线程的区别

进程就是一个应用程序。生活化之后就是一个个的不同的公司,像阿里巴巴、京东......

线程是进程的一个执行单元。像阿里巴巴中的马云、童文红......

一个进程中可以存在一个或者多个线程。

进程A和进程B之间的内存是独立不共享的。像阿里和京东的资源肯定是不共享的。

线程A和线程B之间栈内存独享(多个线程多个栈),堆和方法区内存共享。像马云和童文红都有自己独立的办公地点,但是他们也会共享公司中的一些公共区域,像茶水间、卫生间等等。

并且每个线程的执行是独立的,栈与栈之间各自执行自己的,即多个线程会同时执行,即多线程并发。

所以在多线程并发执行的情况下,main方法执行结束并不代表整个程序的结束。

在Java中,在Dos窗口执行了java Hello,那么此时就有一个Java进程,但是这个进程中又包含了main主线程、垃圾回收线程等等......

多线程有什么好处呢?

多线程并发执行可以提高程序的执行效率。

生活中很简单的例子,在之前还需要去火车站人工售票窗口购买火车票时,多线程就好比是多个售票窗口,那肯定可以增加火车站的运行效率,如果只有一个售票窗口,所有的人都在这一个售票窗口排队买票,可想而知这是多么糟糕的买票体验。

这里又存在了一个问题:对于单核cpu来说,可以做到真正的多线程并发吗?

对于单核的计算机来说,在某个节点只能做一件事情,但是因为计算机处理的速度非常非常的快,并且在多个线程之间频繁的来回切换,就会给人感觉是多件事情同时执行的,但是,这也仅仅是感觉而已。单核cpu是不能做到真正的多线程并发的。

2.实现线程的两种方式

  • 1.继承Thread类

1.书写一个继承java.lang.Thread方法的线程类,重写run方法(需要自己选择重写)

2.创建线程对象:new 线程类

3.启动线程:xxx.start();(会自动的调用重写的run())

​ 在执行到start()时,会启动一个分支线程,在JVM中新开辟一个空间(此时有两个栈),该代码完成该任务之后会瞬间结束!
​ 启动成功的线程会自动的调用重写的run()
​ run()与main()一样在栈的底部,run()和main()同级

​ 此时两个栈是各自独立运行的,但是控制台只有一个,所以它们会争相的输出到控制台,但是顺序每次执行都是不一定的。

例:

package com.dh.thread;

public class Thread01 {

    public static void main(String[] args) {
        //2.创建一个线程对象
        MyThread t = new MyThread();
        //3.启动一个线程
        /*
        重点:
        在执行到start()时,会启动一个分支线程,在JVM中新开辟一个空间(此时有两个栈),该代码完成该任务之后,会瞬间结束!
        启动成功的线程会自动的调用重写的run();
        run()与main()一样在栈的底部,run()和main()同级;
        此时两个栈是各自独立运行的,但是控制台只有一个,所以它们会争相的输出到控制台,但是顺序每次执行都是不一定的。
         */
        t.start();
        //主线程中的代码
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程"+i);
        }
    }
}

//1.书写一个继承Thread类的线程类
class MyThread extends Thread{
    //重写run()
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("子线程"+i);
        }
    }
}

结果:

可以看到,主线程和子线程是交替输出到控制台的,且每次执行,结果都是不一样的。

注意:

只有start()才能开启一个子线程,如果在main中直接调用run(),这就只是一个简单的方法调用,而不是多线程了!而且也必须重写run(),它会在一个栈的底部。


  • 2.实现Runnable接口

1.书写一个类,实现java.lang.Runnable接口,重写run方法(会强制提示重写)

2.创建线程对象:先new一个实现接口的类对象作为参数,然后new Thread(参数)

3.启动线程:xxx.start()

例:

package com.dh.thread;

public class Thread02 {

    public static void main(String[] args) {

        //2.利用实现了Runnable接口的类的对象做为参数创建一个线程
        Thread t = new Thread(new MyThread01());
        //3.1启动线程
        t.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程"+i);
        }
    }
}

//1.书写一个类实现Runnable接口,此时该类就是一个普通的运行类,不是线程类
class MyThread01 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("子线程"+i);
        }
    }
}

结果:

此种方式是面向接口的方式,是更为常用的一种方法。

因为既可以实现多个接口,又可以继承类,更加的灵活。

  • 匿名内部类的实现:(本质就是实现Runnable接口)
package com.dh.thread;

public class Thread03 {

    public static void main(String[] args) {

        //1.采用匿名内部类的形式创建一个线程
        Thread t = new Thread(new Runnable() {
            //2.重写run()
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("子线程"+i);
                }
            }
        });
        //3.启动线程
        t.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程"+i);
        }
    }
}

水平有限,若有错误,望指正~

posted @ 2021-02-23 20:10  deng-hui  阅读(244)  评论(0编辑  收藏  举报