多线程基础

Posted whalesea

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程基础相关的知识,希望对你有一定的参考价值。

一、线程和进程

一般都知道,线程是被包含在进程里的,一个进程可以有多个线程同时存在。

进程是资源分配的最小空间,线程是cpu调度的最小单位。

进程和线程的区别:
1、线程不能看做独立应用,而进程可看做独立应用。
2、进程有独立的地址空间,互相不影响,线程只是进程的不同执行路径。
3、线程没有独立的地址空间,多进程的程序比多线程程序健壮。
4、进程的切换比线程的切换开销大。

java中线程和进程的关系:
1、java对操作系统提供的功能进行封装,包括进程和线程。
2、运行一个java程序会产生一个进程,进程包含至少一个进程。
3、每个进程对应一个JVM实例,多个线程共享JVM里的堆。
4、java采用单线程编程模型,程序会自动创建主线程。
5、主线程可以创建子线程,原则上要后于子线程完成执行。

二、Thread和Runnable,创建线程

Runnable是一个函数式接口,内部只有一个public abstract void run()的抽象方法,所以它本身是不带有多线程的功能的。Thread是实现了Runnable接口的类,它的start方法才使得run()里的代码支持多线程特性。

由于java的单一继承原则,推荐使用Runnable。例如可以将业务类实现Runnable接口,将业务逻辑封装在run方法里,将给业务类作为参数传递给Thread类,可以实现多线程的特性。但是,如果通过继承Thread类的方式实现多线程,那么业务类将无法继承其他类。

另外:Runnable里的资源可以共享,下面使用代码比较说明:

使用Thread:

public class MyThread extends Thread {
    int count = 5;

    @Override
    public void run() {
        while (count > 0){
            System.out.println(Thread.currentThread().getName()+ ":" + count);
            count--;
        }
    }
}
public class ThreadDome {
    public static void main(String[] args){
        Thread thread1 = new MyThread();
        Thread thread2 = new MyThread();
        Thread thread3 = new MyThread();

        thread1.start();
        thread2.start();
        thread3.start();

    }
}

运行结果:

Thread-0:5
Thread-0:4
Thread-0:3
Thread-0:2
Thread-1:5
Thread-1:4
Thread-0:1
Thread-1:3
Thread-1:2
Thread-1:1
Thread-2:5
Thread-2:4
Thread-2:3
Thread-2:2
Thread-2:1

 

 

使用Runnable:

public class MyRunnable implements Runnable {
    private int count = 5;

    @Override
    public void run() {
        while (count > 0){
            System.out.println(Thread.currentThread().getName() +":"+ count);
            count--;
        }
    }
}
public class RunnableDome {
    public static void main(String[] args){
        MyRunnable myRunnable1 = new MyRunnable();

        Thread thread1 = new Thread(myRunnable1);
        Thread thread2 = new Thread(myRunnable1);
        Thread thread3 = new Thread(myRunnable1);

        thread1.start();
        thread2.start();
        thread3.start();

    }
}

结果:

Thread-0:5
Thread-1:5
Thread-0:4
Thread-0:2
Thread-1:3
Thread-0:1

三、Thread类中的Run和Start方法的区别

先说一下结论:它们都可以实现启动run方法里的逻辑,区别是,run方法调用的主线程,start方法调用的是新创建的线程。

 

1、代码说明:

先是run方法:

public class ThreadTest {
    public static void attack(){
        System.out.println("Current thread is :" + Thread.currentThread().getName());
    }

    public static void main(String[] args){
        Thread r = new Thread(){
            @Override
            public void run() {
                attack();
            }
        };
        System.out.println("Current main thread is :" + Thread.currentThread().getName());
        r.run();
    }
}

结果:

Current main thread is :main
Current thread is :main

可以发现,当前线程是main

 

start方法:

public class ThreadTest {
    public static void attack(){
        System.out.println("Current thread is :" + Thread.currentThread().getName());
    }

    public static void main(String[] args){
        Thread r = new Thread(){
            @Override
            public void run() {
                attack();
            }
        };
        System.out.println("Current main thread is :" + Thread.currentThread().getName());
        r.start();
    }
}

结果:

Current main thread is :main
Current thread is :Thread-0

可以发现,当前线程是新创建的Thread-0.

2、为什么会这样?

首先,run方法是Runnable的一个抽象方法,Thread类重写这个方法的源码如下:

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

target就就是当做参数传入的Runnable接口。如果是通过参入Runnable 参数实现的Thread,就调用Runnable的run方法;而如果没有传入Runnable接口,那么targer会为空,这时意味着,run方法被Thread的继承类重写或是被匿名内部类重写,则会调用相应的重写run方法。

但是,通过上述代码没有找到新线程的创建的逻辑,是因为,新线程的创建在Thread类的start方法里,代码:

   public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group‘s list of threads
         * and the group‘s unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

继续查看start0的方法:

    private native void start0();

这里是调用了外部的源码,继续查找(查看源码网址:链接):

 {"start0",           "()V",        (void *)&JVM_StartThread},

调用jvm包里的JVM_StartThread方法(链接):

其中关键:

native_thread = new JavaThread(&thread_entry, sz);

继续查看:thread_entry方法:

static void thread_entry(JavaThread* thread, TRAPS) {
  HandleMark hm(THREAD);
  Handle obj(THREAD, thread->threadObj());
  JavaValue result(T_VOID);
  JavaCalls::call_virtual(&result,
                          obj,
                          KlassHandle(THREAD, SystemDictionary::Thread_klass()),
                          vmSymbols::run_method_name(),
                          vmSymbols::void_method_signature(),
                          THREAD);
}

最终在创建线程之后,调用run方法(vmSymbols::run_method_name())。

所以start的逻辑就是:先调用底层代码创建线程,然后返回调用run方法。

 

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

线程学习知识点总结

号称史上最全Java多线程与并发面试题总结—基础篇

多个请求是多线程吗

java基础入门-多线程同步浅析-以银行转账为样例

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

多线程编程