java多线程总结 - 基础篇
进程与线程区别
进程是资源分配的最小单位,进程中会有多个线程。引入线程的目的是因为计算机cpu上下文切换会很频繁,而进程的上下文切换相对耗时耗资源。
而线程是在进程中的,线程是cpu调度的最小单位,进程中的多个线程共享一块内存。线程的上下文切换成本更低。
线程的状态
New、Runnable(包含运行态和就绪态)、Waiting、TimeWaiting、Blocked、Terminated
线程的start与run方法的区别
start:新创建一个线程并执行run方法
run:在当前线程执行run方法
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's list of threads * and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
// 执行本地方法,新创建一个线程,并执行run方法
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();
JVM对应的源码:http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/63dd5b7d50eb/src/share/native/java/lang/Thread.c
调用了JVM_StartThread方法:
{ "start0", "()V", (void *)&JVM_StartThread},
http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/176a7e5cc060/src/share/vm/prims/jvm.cpp
JVM_StartThread方法,new JavaThread(&thread_entry, sz):
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
// We cannot hold the Threads_lock when we throw an exception,
// due to rank ordering issues. Example: we might need to grab the
// Heap_lock while we construct the exception.
bool throw_illegal_thread_state = false;
// We must release the Threads_lock before we can post a jvmti event
// in Thread::start.
{
// Ensure that the C++ Thread and OSThread structures aren't freed before
// we operate.
MutexLocker mu(Threads_lock);
// Since JDK 5 the java.lang.Thread threadStatus is used to prevent
// re-starting an already started thread, so we should usually find
// that the JavaThread is null. However for a JNI attached thread
// there is a small window between the Thread object being created
// (with its JavaThread set) and the update to its threadStatus, so we
// have to check for this
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
} else {
// We could also check the stillborn flag to see if this thread was already stopped, but
// for historical reasons we let the thread detect that itself when it starts running
jlong size =
java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
// Allocate the C++ Thread structure and create the native thread. The
// stack size retrieved from java is signed, but the constructor takes
// size_t (an unsigned type), so avoid passing negative values which would
// result in really large stacks.
size_t sz = size > 0 ? (size_t) size : 0;
// !!!! 调用了JVM_StartThread的new JavaThread
native_thread = new JavaThread(&thread_entry, sz);
// At this point it may be possible that no osthread was created for the
// JavaThread due to lack of memory. Check for this situation and throw
// an exception if necessary. Eventually we may want to change this so
// that we only grab the lock if the thread was created successfully -
// then we can also do this check and throw the exception in the
// JavaThread constructor.
if (native_thread->osthread() != NULL) {
// Note: the current thread is not being used within "prepare".
native_thread->prepare(jthread);
}
}
}
run方法源码:单纯在当前线程中执行run方法
/** * 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();
}
}
创建线程有哪些方式?
1、继承Thread类(Thread类也实现了Runnable接口),重写run方法
2、实现Runnable接口,重写run方法,Thread threadA = new Thread(Runnable r);
两者本质上都是创建Thread对象,通过start方法,创建一个新线程并执行thread对象的run方法。
如何处理线程的返回值?
可以通过JUC包下的FutureTask,我们知道Thread的run方法是没有返回值的,而FutureTask实现了Callable接口,同时也实现了Runnable接口。Callable接口是有返回值的。
public interface Callable<V> {
/** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */
V call() throws Exception;
}
FutureTask通过实现Callable接口,将Runnable的run方法关联起来,并将线程的返回值通过set()方法保存在一个outcome的对象中。通过get()方法可以获取到这个返回值。
下面是FutureTask类的run方法部分代码:
public void run() {
try {
// 取得callable的实现
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 执行call方法,将返回值存在临时变量result
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
// 将result保存到outcome字段
set(result);
}
}
}
//保存到outcome字段
protected void set(V v) {
outcome = v;
}
sleep与wait的区别
sleep是Thread类的方法,wait是Object类的方法。
sleep方法可以随时被调用,而wait方法只能在synchronized中使用(你只有持有锁,才能释放锁)
sleep是定时等待,有时间设定,在此期间,线程只释放cpu,不释放同步资源锁。
wait是无限期等待,释放cpu,同时释放所持有的的锁,只有等notify或notifyAll时被唤醒。
notify与notifyAll的区别
锁池:当一个线程获取到对象锁之后,其他线程去竞争这把锁之后,会进入阻塞状态,并进入锁池。等待被唤醒。
notify:随机唤醒一个阻塞线程去竞争锁
notifyAll:唤醒所有阻塞线程,去竞争锁
yield函数
/** * A hint to the scheduler that the current thread is willing to yield * its current use of a processor. The scheduler is free to ignore this * hint. * * <p> Yield is a heuristic attempt to improve relative progression * between threads that would otherwise over-utilise a CPU. Its use * should be combined with detailed profiling and benchmarking to * ensure that it actually has the desired effect. * * <p> It is rarely appropriate to use this method. It may be useful * for debugging or testing purposes, where it may help to reproduce * bugs due to race conditions. It may also be useful when designing * concurrency control constructs such as the ones in the * {@link java.util.concurrent.locks} package. */
public static native void yield();
yield函数主要用于告诉cpu,我愿意将cpu资源让给其他线程,但这也可能被调度器忽略。主要用于多线程锁相关的问题。
interrupt函数
interrupt是中断的意思,例如我们鼠标和键盘向计算机输入信息,就是通过中断实现的。这里的interrupt是向线程发起一个中断信号,这样就可以将线程进行标记。如果接收到终端信号的线程处于阻塞状态,则抛出InterruptException异常,后续可以对该线程的中断进行相应的处理。
还没有评论,来说两句吧...