Java应用启动创建大量线程_Java并发之线程的使用以及构建启动线程

构建线程

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

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

当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法)。Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:

1.调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。

2.非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常。

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

源码参考如下:/**

* A thread is a thread of execution in a program. The Java

* Virtual Machine allows an application to have multiple threads of

* execution running concurrently.

*

* 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 Thread 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.

*

* When a Java Virtual Machine starts up, there is usually a single

* non-daemon thread (which typically calls the method named

* main of some designated class). The Java Virtual

* Machine continues to execute threads until either of the following

* occurs:

*

*

The exit method of class Runtime has been

* called and the security manager has permitted the exit operation

* to take place.

*

All threads that are not daemon threads have died, either by

* returning from the call to the run method or by

* throwing an exception that propagates beyond the run

* method.

*

*

* There are two ways to create a new thread of execution. One is to

* declare a class to be a subclass of Thread. This

* subclass should override the run method of class

* Thread. 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:

*


 
  

* 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 {

需要的信息

在运行线程之前首先要构造一个线程对象,线程对象在构造的时候需要提供线程所需要的属性,如线程所属的线程组、线程优先级、是否是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'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'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();

}

结论

一个新构建的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实现的多线程");

}

}

说明

通过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接口实现的多线程");

}

}

使用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{

//需要实现call方法而不是run方法

@Override public Integer call() throws Exception { return 100;

}

}

启动线程

通过源码分析得出: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 run method of this thread.

*

* The result is that two threads are running concurrently: the

* current thread (which returns from the call to the

* start method) and the other thread (which executes its

* run method).

*

* 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'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 */

}

}

}

private native void start0();

参考代码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 myCallable = new MyCallable();

//使用FutureTask来包装MyCallable对象

FutureTask ft = new FutureTask(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 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();

}

}

}

启动线程的注意事项53e00c514b665982d523fea96f9f0510.png

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

Thread与Runnable的关系

实现关系

Thread实现接口Runnable,并且实现了run方法,代码参考如下://如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;

//否则,该方法不执行任何操作并返回。

//Thread 的子类应该重写该方法。

/**

* If this thread was constructed using a separate

* Runnable run object, then that

* Runnable object's run method is called;

* otherwise, this method does nothing and returns.

*

* Subclasses of Thread should override this method.

*

* @see #start()

* @see #stop()

* @see #Thread(ThreadGroup, Runnable, String)

*/

@Override

public void run() { if (target != null) {

target.run();

}

}

}

区别

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值