一、创建线程
1.使用Thread类
Thread t = new Thread(){
//创建线程对象,重写run方法
@Override
public void run() {
//此处是该线程要执行的内容
log.debug("Running");
}
};
t.setName("t1");
t.start();
2.使用Runnable接口
Runnable runnable = new Runnable() {
@Override
public void run() {
log.debug("Running");
}
};
Thread t = new Thread(runnable);
t.setName("t1");
t.start();
由于Runnable接口中只有一个方法,并且含有@FunctionalInterface注解,所以Runnable接口为函数式接口,可以使用lambda表达式简化代码,如下:
Thread t = new Thread(() -> log.debug("running"),"t1");
t.start();
Thread与Runnable之间的关系
首先我们创建Runnable对象时,重写的是Runnable接口自身的run()方法:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
当我们把Runnable对象传入Thread构造方法时,会调用init()方法,并将Runnable对象传入:
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, target, name)}.
*
* @param target
* the object whose {@code run} method is invoked when this thread
* is started. If {@code null}, this thread's run method is invoked.
*
* @param name
* the name of the new thread
*/
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
进入init()方法后,我们会发现Thread重载了init()方法,继续传入Runnable对象
/**
* Initializes a Thread with the current AccessControlContext.
* @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
再次进入重载后的init()方法,我们发现会将Runnable对象传给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
* @param inheritThreadLocals if {@code true}, inherit initial values for
* inheritable thread-locals from the constructing thread
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
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;
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();
// 此处将我们之前传入的Runnable对象赋给Thread类中的target
this.target = target;
setPriority(priority);
if (inheritThreadLocals && 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 */
tid = nextThreadID();
}
最后我们发现,最终还是执行Thread类中的run方法,如果Runnable对象不为空,则会执行。
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object'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();
}
}
3.使用FutureTask创建线程并获取执行结果
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask(() -> {
log.debug("Running");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "执行成功";
});
Thread t1 = new Thread(futureTask,"t1");
t1.start();
//获取执行结果,等待t1线程执行完成,会使main线程进入阻塞状态。
String result = futureTask.get();
log.debug("{}",result);
}
二、多线程执行现象
public static void main(String[] args) {
//开启两个线程
new Thread(() -> {
while (true) {
log.debug("Running");
}
}, "t1").start();
new Thread(() -> {
while (true) {
log.debug("Running");
}
}, "t2").start();
}
输出结果:
21:55:06.733 DEBUG [t2] com.dxf.线程执行现象.Test1 - Running
21:55:06.733 DEBUG [t2] com.dxf.线程执行现象.Test1 - Running
21:55:06.733 DEBUG [t2] com.dxf.线程执行现象.Test1 - Running
21:55:06.733 DEBUG [t2] com.dxf.线程执行现象.Test1 - Running
21:55:06.733 DEBUG [t2] com.dxf.线程执行现象.Test1 - Running
21:55:06.733 DEBUG [t2] com.dxf.线程执行现象.Test1 - Running
21:55:06.733 DEBUG [t1] com.dxf.线程执行现象.Test1 - Running
21:55:06.733 DEBUG [t1] com.dxf.线程执行现象.Test1 - Running
21:55:06.733 DEBUG [t1] com.dxf.线程执行现象.Test1 - Running
21:55:06.733 DEBUG [t1] com.dxf.线程执行现象.Test1 - Running
结论:两条线程会并行交替执行,谁先谁后不由我们控制,由底层的任务调度器来决定的。
网友评论