Jadual Kandungan
构建线程
Thread说明
需要的信息
结论
构建的方式
继承Thread
代码
说明
实现接口Runnable
使用Callable、Future实现有返回结果的多线程
启动线程
参考代码
启动线程的注意事项
Thread与Runnable的关系
实现关系
区别
Rumah Java javaTutorial Java并发之线程的使用以及构建启动线程

Java并发之线程的使用以及构建启动线程

Sep 30, 2017 am 10:38 AM
java guna


构建线程

Thread说明

  • 线程是程序中的执行线程,java虚拟机允许应用程序并发的运行多个线程。

  • 每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。

  • 当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法)。Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:  
               1.调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。  
               2.非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常。

  • 实现线程的方式,会在后续的章节中介绍

源码参考如下:

/**
 * A <i>thread</i> is a thread of execution in a program. The Java
 * Virtual Machine allows an application to have multiple threads of
 * execution running concurrently.
 * <p>
 * Every thread has a priority. Threads with higher priority are
 * executed in preference to threads with lower priority. Each thread
 * may or may not also be marked as a daemon. When code running in
 * some thread creates a new <code>Thread</code> object, the new
 * thread has its priority initially set equal to the priority of the
 * creating thread, and is a daemon thread if and only if the
 * creating thread is a daemon.
 * <p>
 * When a Java Virtual Machine starts up, there is usually a single
 * non-daemon thread (which typically calls the method named
 * <code>main</code> of some designated class). The Java Virtual
 * Machine continues to execute threads until either of the following
 * occurs:
 * <ul>
 * <li>The <code>exit</code> method of class <code>Runtime</code> has been
 *     called and the security manager has permitted the exit operation
 *     to take place.
 * <li>All threads that are not daemon threads have died, either by
 *     returning from the call to the <code>run</code> method or by
 *     throwing an exception that propagates beyond the <code>run</code>
 *     method.
 * </ul>
 * <p>
 * There are two ways to create a new thread of execution. One is to
 * declare a class to be a subclass of <code>Thread</code>. This
 * subclass should override the <code>run</code> method of class
 * <code>Thread</code>. An instance of the subclass can then be
 * allocated and started. For example, a thread that computes primes
 * larger than a stated value could be written as follows:
 * <hr><blockquote><pre class="brush:php;toolbar:false">
 *     class PrimeThread extends Thread {
 *         long minPrime;
 *         PrimeThread(long minPrime) {
 *             this.minPrime = minPrime;
 *         }
 *
 *         public void run() {
 *             // compute primes larger than minPrime
 *              . . .
 *         }
 *     }
 * 

*

* The following code would then create a thread and start it running: *

 *     PrimeThread p = new PrimeThread(143);
 *     p.start();
 * 
*

* The other way to create a thread is to declare a class that * implements the Runnable interface. That class then * implements the run method. An instance of the class can * then be allocated, passed as an argument when creating * Thread, and started. The same example in this other * style looks like the following: *


 *     class PrimeRun implements Runnable {
 *         long minPrime;
 *         PrimeRun(long minPrime) {
 *             this.minPrime = minPrime;
 *         }
 *
 *         public void run() {
 *             // compute primes larger than minPrime
 *              . . .
 *         }
 *     }
 * 

*

* The following code would then create a thread and start it running: *

 *     PrimeRun p = new PrimeRun(143);
 *     new Thread(p).start();
 * 
*

* Every thread has a name for identification purposes. More than * one thread may have the same name. If a name is not specified when * a thread is created, a new name is generated for it. *

* Unless otherwise noted, passing a {@code null} argument to a constructor * or method in this class will cause a {@link NullPointerException} to be * thrown. * * @author unascribed * @see Runnable * @see Runtime#exit(int) * @see #run() * @see #stop() * @since JDK1.0 */ publicclass Thread implements Runnable {

Salin selepas log masuk

需要的信息

在运行线程之前首先要构造一个线程对象,线程对象在构造的时候需要提供线程所需要的属性,如线程所属的线程组、线程优先级、是否是Daemon线程等信息。在new Thread时会调用以下方法进行实例化Thread对象。
初始化代码如下:

    /**
     * Initializes a Thread.
     *
     * @param g the Thread group
     * @param target the object whose run() method gets called
     * @param name the name of the new Thread
     * @param stackSize the desired stack size for the new thread, or
     *        zero to indicate that this parameter is to be ignored.
     * @param acc the AccessControlContext to inherit, or
     *            AccessController.getContext() if null
     */
    private void init(ThreadGroup g, Runnable target, String name,                      
    long stackSize, AccessControlContext acc) {        
    if (name == null) {            
    throw new NullPointerException("name cannot be null");
        }        
        this.name = name;        //当前线程作为该线程的父线程
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();        //线程组的获取:如果传入的参数为空首先获取系统默认的安全组,如果为空获取父线程的安全组
        if (g == null) {            
        /* Determine if it&#39;s an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }            /* If the security doesn&#39;t have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();        /*
         * Do we have the required permissions?
         */
        if (security != null) {            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();        
        this.group = g;        //设置daemon 、priority 属性为父线程对应的值
        this.daemon = parent.isDaemon();        
        this.priority = parent.getPriority();        
        
        if (security == null || isCCLOverridden(parent.getClass()))            
        this.contextClassLoader = parent.getContextClassLoader();        
        else
            this.contextClassLoader = parent.contextClassLoader;        
            this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();        
                this.target = target;
        setPriority(priority);        //将父线程的InheritableThreadLocal复制过来
        if (parent.inheritableThreadLocals != null)            
        this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);        
                /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;        /* Set thread ID */ 
        //生成线程id(一个long型的字段threadSeqNumber)
        tid = nextThreadID();
    }
Salin selepas log masuk

结论

一个新构建的Thread对象(new Thread()),是由其父线程(当前线程)进行空间分配,而子线程继承了父线程的Daemon、优先级和加载资源的contextClassLoader,以及可继承的ThreadLocal,同时会为子线程分配一个线程id。一个可以运行的线程对象完成初始化工作,并且在堆内存中等待运行。

构建的方式

继承Thread

代码

//方法1通过继承Thread实现class MyThread extends Thread{

    //需要实现的方法,该方法执行具体的业务逻辑
    @Override    public void run() {
        System.out.println(Thread.currentThread().getName()
                +" @@@@ MyThread。run()我是通过继承Thread实现的多线程");
    }

}
Salin selepas log masuk

说明

通过Thread源码发现(Thread implements Runnable)发现thread其实也是一个实现了runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。

其中run()方法的方法体代表了线程需要完成的任务,称之为线程执行体。当创建此线程类对象时一个新的线程得以创建,并进入到线程新建状态。通过调用线程对象引用的start()方法,使得该线程进入到就绪状态,此时此线程并不一定会马上得以执行,这取决于CPU调度时机。

实现接口Runnable

代码

//方法2通过实现runnable接口
//实现Runnable接口,并重写该接口的run()方法,该run()方法同样是线程执行体,创建Runnable实现类的实例,
//并以此实例作为Thread类的target来创建Thread对象,该Thread对象才是真正的线程对象。class MyRunnable implements Runnable{

    @Override    public void run() {
        System.out.println(Thread.currentThread().getName()+                
        " @@@@ MyRunnable。run()我是通过实现Runnable接口实现的多线程");
    }

}
Salin selepas log masuk

使用Callable、Future实现有返回结果的多线程

使用Callable和Future接口创建线程。具体是创建Callable接口的实现类,并实现clall()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。
可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了,再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了(关于Executor的使用后续的文章中详细介绍。)。

//方法3通过Executor框架实现class MyCallable implements Callable<Integer>{
    //需要实现call方法而不是run方法
    @Override    public Integer call() throws Exception {        return 100;
    }
}
Salin selepas log masuk

启动线程

通过源码分析得出:

  • 1.对象初始化完成之后,通过执行start方法来执行这个线程,并且java虚拟机会调用该线程的run方法执行线程的业务逻辑;

  • 2.调用start方法之后发现会同时有两个线程在执行:当前线程(parent线程【同步告知java虚拟机,只要线程规划器空闲,应立即启动调用start方法的线程】,从调用返回给start方法)和另一个线程(执行其run方法)。

  • 3.并且多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。

    start方法源码说明如下:

   /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.
     * @see        #run()
     * @see        #stop()
     */
    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&#39;s list of threads
         * and the group&#39;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 */
            }
        }
    }

    private native void start0();
Salin selepas log masuk

参考代码

public class TestCreateThread {    public static void main(String[] args) {
        Thread myThread = new MyThread();
        myThread.setName("myThread");
        myThread.start();

        Runnable myRunnable = new MyRunnable();
        Thread myRunnableThread = new Thread(myRunnable);
        myRunnableThread.setName("myRunnableThread");
        myRunnableThread.start();

        Thread myRunnableThread2 = new MyThread(myRunnable);
        myRunnableThread2.setName("myRunnableThread2");
        myRunnableThread2.start();        //执行结果参考如下:
        //myThread @@@@ MyThread。run()我是通过继承Thread实现的多线程
        //myRunnableThread2 @@@@ MyThread。run()我是通过继承Thread实现的多线程
        //myRunnableThread @@@@ MyRunnable。run()我是通过实现Runnable接口实现的多线程

        //测试callable方法
        // 创建MyCallable对象
        Callable<Integer> myCallable = new MyCallable();    
        //使用FutureTask来包装MyCallable对象
        FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); 
        //FutureTask对象作为Thread对象的target创建新的线程
        Thread thread = new Thread(ft);
        thread.start();//启用

        //获取信息
        try {            //取得新创建的新线程中的call()方法返回的结果
            //当子线程此方法还未执行完毕,ft.get()方法会一直阻塞,
            //直到call()方法执行完毕才能取到返回值。
            int sum = ft.get();
            System.out.println("sum = " + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }        //使用ExecutorService处理多线程
        ExecutorService pool = Executors.newFixedThreadPool(10);  
        Future<Integer> f = pool.submit(myCallable);  
        // 关闭线程池  
        pool.shutdown(); 
        try {            int sum1 = f.get();
            System.out.println("sum1 = " + sum1);
        } catch (InterruptedException e) {            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
Salin selepas log masuk

启动线程的注意事项


这里写图片描述


无论何种方式,启动一个线程,就要给它一个名字!这对排错诊断系统监控有帮助。否则诊断问题时,无法直观知道某个线程的用途。

Thread与Runnable的关系

实现关系

Thread实现接口Runnable,并且实现了run方法,代码参考如下:

        //如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;
        //否则,该方法不执行任何操作并返回。
        //Thread 的子类应该重写该方法。
        /**
         * If this thread was constructed using a separate
         * <code>Runnable</code> run object, then that
         * <code>Runnable</code> object&#39;s <code>run</code> method is called;
         * otherwise, this method does nothing and returns.
         * <p>
         * Subclasses of <code>Thread</code> should override this method.
         *
         * @see     #start()
         * @see     #stop()
         * @see     #Thread(ThreadGroup, Runnable, String)
         */
        @Override
        public void run() {            if (target != null) {
                target.run();
            }
        }

}
Salin selepas log masuk

区别

      当执行到Thread类中的run()方法时,会首先判断target是否存在,存在则执行target中的run()方法,也就是实现了Runnable接口并重写了run()方法的类中的run()方法。当时如果该Runnable的子类是通过一个继承Thread的子类(该且重写了run方法),则真正执行的是Thread子类重写的run方法(由于多态的原因)。

Atas ialah kandungan terperinci Java并发之线程的使用以及构建启动线程. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Akan R.E.P.O. Ada Crossplay?
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Nombor Sempurna di Jawa Nombor Sempurna di Jawa Aug 30, 2024 pm 04:28 PM

Panduan Nombor Sempurna di Jawa. Di sini kita membincangkan Definisi, Bagaimana untuk menyemak nombor Perfect dalam Java?, contoh dengan pelaksanaan kod.

Weka di Jawa Weka di Jawa Aug 30, 2024 pm 04:28 PM

Panduan untuk Weka di Jawa. Di sini kita membincangkan Pengenalan, cara menggunakan weka java, jenis platform, dan kelebihan dengan contoh.

Nombor Smith di Jawa Nombor Smith di Jawa Aug 30, 2024 pm 04:28 PM

Panduan untuk Nombor Smith di Jawa. Di sini kita membincangkan Definisi, Bagaimana untuk menyemak nombor smith di Jawa? contoh dengan pelaksanaan kod.

Soalan Temuduga Java Spring Soalan Temuduga Java Spring Aug 30, 2024 pm 04:29 PM

Dalam artikel ini, kami telah menyimpan Soalan Temuduga Spring Java yang paling banyak ditanya dengan jawapan terperinci mereka. Supaya anda boleh memecahkan temuduga.

Cuti atau kembali dari Java 8 Stream Foreach? Cuti atau kembali dari Java 8 Stream Foreach? Feb 07, 2025 pm 12:09 PM

Java 8 memperkenalkan API Stream, menyediakan cara yang kuat dan ekspresif untuk memproses koleksi data. Walau bagaimanapun, soalan biasa apabila menggunakan aliran adalah: bagaimana untuk memecahkan atau kembali dari operasi foreach? Gelung tradisional membolehkan gangguan awal atau pulangan, tetapi kaedah Foreach Stream tidak menyokong secara langsung kaedah ini. Artikel ini akan menerangkan sebab -sebab dan meneroka kaedah alternatif untuk melaksanakan penamatan pramatang dalam sistem pemprosesan aliran. Bacaan Lanjut: Penambahbaikan API Java Stream Memahami aliran aliran Kaedah Foreach adalah operasi terminal yang melakukan satu operasi pada setiap elemen dalam aliran. Niat reka bentuknya adalah

TimeStamp to Date in Java TimeStamp to Date in Java Aug 30, 2024 pm 04:28 PM

Panduan untuk TimeStamp to Date di Java. Di sini kita juga membincangkan pengenalan dan cara menukar cap waktu kepada tarikh dalam java bersama-sama dengan contoh.

Program Java untuk mencari kelantangan kapsul Program Java untuk mencari kelantangan kapsul Feb 07, 2025 am 11:37 AM

Kapsul adalah angka geometri tiga dimensi, terdiri daripada silinder dan hemisfera di kedua-dua hujungnya. Jumlah kapsul boleh dikira dengan menambahkan isipadu silinder dan jumlah hemisfera di kedua -dua hujungnya. Tutorial ini akan membincangkan cara mengira jumlah kapsul yang diberikan dalam Java menggunakan kaedah yang berbeza. Formula volum kapsul Formula untuk jumlah kapsul adalah seperti berikut: Kelantangan kapsul = isipadu isipadu silinder Dua jumlah hemisfera dalam, R: Radius hemisfera. H: Ketinggian silinder (tidak termasuk hemisfera). Contoh 1 masukkan Jejari = 5 unit Ketinggian = 10 unit Output Jilid = 1570.8 Unit padu menjelaskan Kirakan kelantangan menggunakan formula: Kelantangan = π × r2 × h (4

Bagaimana untuk menjalankan aplikasi boot musim bunga pertama anda di Spring Tool Suite? Bagaimana untuk menjalankan aplikasi boot musim bunga pertama anda di Spring Tool Suite? Feb 07, 2025 pm 12:11 PM

Spring Boot memudahkan penciptaan aplikasi Java yang mantap, berskala, dan siap pengeluaran, merevolusi pembangunan Java. Pendekatan "Konvensyen Lebih Konfigurasi", yang wujud pada ekosistem musim bunga, meminimumkan persediaan manual, Allo

See all articles