您的位置:首页 > 其它

线程创建方式,最后一种打赌你不知道

2021-02-26 21:34 369 查看

Java创建线程的几种方式

创建线程,是在多线程编程中最基本的操作,熟悉Java线程的应该很清楚有几种创建线程的方式。有关此类文章,也已经被网友们说烂了,八仙过海各显神通,甚至lambda表达式写法都被说了,但是说来说去,我发现只有一种没有被说,所以今天来挖掘一下新技能,也就是介绍这个“神秘”的办法,这种办法,多数人肯定不知道,因为不常用,但是也可以使实现,在此之前,我们先预习一下Java创建线程的方式。

继承Thread

这种方式有个缺点就是不能继承其他类了。

public class ThreadTest {
    public static void main(String[] args) {

        new MyThread().start();
    }
    static class MyThread extends Thread{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
}

实现Runnable接口

这种方式可以做到数据共享,即只创建一个Runnable,然后实例化多个Thread,并把Runnable传递进去。


package com.company;

public class ThreadTest {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        for (int i = 0; i < 10; i++) {
            new Thread(myThread).start();
        }
    }
    static class MyThread implements Runnable{
        private int total;
        private Object mutex =new Object();
        @Override
        public void run() {
            synchronized (mutex){
                for (int i = 0; i < 100; i++) {
                    total++;
                }
            }
            System.out.println(Thread.currentThread().getName() +"  "+total);
        }
    }
}
输出:
Thread-0  100
Thread-2  300
Thread-3  400
Thread-1  200
Thread-4  500
Thread-5  600
Thread-6  700
Thread-7  800
Thread-8  900
Thread-9  1000

FutureTask和Callable

使用这种办法,还可以阻塞到任务完成,并且还是有返回值的。

public class ThreadTest {
    public static void main(String[] args) {

        FutureTask<String> stringFutureTask = new FutureTask<>(new MyCallable());

        new Thread(stringFutureTask).start();

    }

    static class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            System.out.println(Thread.currentThread().getName());
            return "OK";
        }
    }
}

Executors

还有就是使用线程池了,submit方法可以提交Callable,也可以提交Runnable。

public class ThreadTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.submit(new MyCallable());

        executorService.shutdown();

    }

    static class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            System.out.println(Thread.currentThread().getName());
            return "OK";
        }
    }
}

JNI

到了最后一种了,就是JNI调用,由C创建线程,线程中调用Runnable的run方法,也可以自定义一个对象,这种情况一般是用不到,但是在系统级别的地方可以用到,用来做回调处理,比如c中下载文件,回调进度的时候就需要在c中调用java对象的方法,另外JVM底层创建线程也是通过这种方式,即调用pthread_create函数。

一来一去,就实现了线程创建,下面在Linux平台下演示。

  1. 编写Java类startThread方法就是由c去实现,参数Runnable作为线程执行的地方。
package com.company;
public class JniThread {
    public final native String startThread(Runnable runnable);
}
  1. 生成h文件。
javah -jni -classpath /home/HouXinLin/projects/java/Idea/Java-Project/ThreadDemo/out/production/ThreadDemo -d ./jni com.company.JniThread
  1. startThread实现。
#include "library.h"
#include "com_company_JniThread.h"
#include <pthread.h>
#include <stdio.h>

JavaVM *g_jvm = NULL;
jobject g_obj = NULL;

void *startThread(void *arg) {
    int status;
    JNIEnv *_jniEnv = NULL;
    status = (*g_jvm)->GetEnv(g_jvm, (void **) &_jniEnv, JNI_VERSION_1_8);

    if (status < 0) {
        status = (*g_jvm)->AttachCurrentThread(g_jvm, (void **) &_jniEnv, NULL);
    }
    jclass runnableClass= (*_jniEnv)->FindClass(_jniEnv,"java/lang/Runnable");
    jmethodID  runId =(*_jniEnv)->GetMethodID(_jniEnv,runnableClass,"run","()V");
    (*_jniEnv)->CallVoidMethod(_jniEnv,arg,runId);

    (*g_jvm)->DetachCurrentThread(g_jvm);
    return 0;
}
JNIEXPORT jstring JNICALL Java_com_company_JniThread_startThread
        (JNIEnv *env, jobject job1, jobject job2) {

    g_obj = (*env)->NewGlobalRef(env, job2);
    pthread_t pthread;
    pthread_create(&pthread, NULL, startThread, g_obj);
    return (*env)->NewStringUTF(env, "OK");
}


jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    g_jvm = vm;
    return JNI_VERSION_1_8;
}

  1. 测试
package com.company;

public class Main {

    public static void main(String[] args) throws InterruptedException {
        String strLibPath = "/home/HouXinLin/projects/c/JniThread/libtest.so";
        System.load(strLibPath);

        JniThread jniThread = new JniThread();
        jniThread.startThread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });

        Thread.sleep(2000);
    }
}

所以有什么用呢?

答:面试装x


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: