
學習消息機制后,有幾個關于線程的問題。于是有了這篇文章。本文絕大多數是對于 Java多線程編程核心技術的總結
什么是線程
執(zhí)行程序中的一個線程,Java虛擬機允許應用程序同時運行多個執(zhí)行線程。每個線程都有優(yōu)先權。具有較高優(yōu)先級的線程優(yōu)先于具有較低優(yōu)先級的線程執(zhí)行。每個線程可能會被標記為守護進程。當在某個線程中運行的代碼創(chuàng)建一個新Thread對象時,新線程的優(yōu)先級最初設置為等于創(chuàng)建線程的優(yōu)先級,并且當且僅當創(chuàng)建線程是守護進程時才是守護進程線程。
線程和進程的關系

Thread的狀態(tài)(有待補充)

線程的啟動
如何實現(xiàn)Thread
* public class Thread implements Runnable
* public interface Runnable
繼承Thread或者實現(xiàn)Runnable接口
public interface Runnable {
public abstract void run();
}
我們來看一下Runnable實現(xiàn)類Thread中的run方法。
@Override
public void run() {
if (target != null) {//target是如何初始化?
target.run();
}
}
//線程初始化
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
//currentThread():返回當前正在執(zhí)行線程的引用
Thread parent = currentThread();
if (g == null) {
g = parent.getThreadGroup();//線程終止返回null,否則返回ThreadGroup對象
}
g.addUnstarted();//記錄未啟動的線程
this.group = g;
this.target = target;
//線程的優(yōu)先級具有繼承關系
this.priority = parent.getPriority();
//是否是守護線程(后面會提到)
this.daemon = parent.isDaemon();
setName(name);
init2(parent);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
tid = nextThreadID();
}
我們看一下Thread的初始化。
public Thread() {
//target為null,run()方法怎么執(zhí)行?
init(null, null, "Thread-" + nextThreadNum(), 0);
}
通過上面的分析我們知道了Thread初始化執(zhí)行了那些操作。但是run方法是怎么被執(zhí)行的?
我們看一下start()方法源碼。
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".
*/
//不能多次調用start()方法,否則拋出IllegalThreadStateException
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. */
//添加線程到ThreadGroup中
group.add(this);
started = false;
try {
//調用run方法
nativeCreate(this, stackSize, daemon);
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 */
}
}
}
我們看到最終都會去調用c++層的nativeCreate()。
static void Thread_nativeCreate(JNIEnv* env, jclass, jobject java_thread, jlong stack_size,
jboolean daemon) {
.
.
.
Thread::CreateNativeThread(env, java_thread, stack_size, daemon == JNI_TRUE);
}
CreateNativeThread(JNIEnv*env, jobject java_peer, size_t stack_size, bool is_daemon) {
.
.
.
if (child_jni_env_ext.get() != nullptr) {
pthread_t new_pthread;
pthread_attr_t attr;
child_thread -> tlsPtr_.tmp_jni_env = child_jni_env_ext.get();
CHECK_PTHREAD_CALL(pthread_attr_init, ( & attr), "new thread");
CHECK_PTHREAD_CALL(pthread_attr_setdetachstate, ( & attr, PTHREAD_CREATE_DETACHED),
"PTHREAD_CREATE_DETACHED");
CHECK_PTHREAD_CALL(pthread_attr_setstacksize, ( & attr, stack_size), stack_size);
pthread_create_result = pthread_create( & new_pthread,
&attr,
Thread::CreateCallback,
child_thread);
CHECK_PTHREAD_CALL(pthread_attr_destroy, ( & attr), "new thread");
if (pthread_create_result == 0) {
// pthread_create started the new thread. The child is now responsible for managing the
// JNIEnvExt we created.
// Note: we can't check for tmp_jni_env == nullptr, as that would require synchronization
// between the threads.
child_jni_env_ext.release();
return;
}
}
// Either JNIEnvExt::Create or pthread_create(3) failed, so clean up.
{
MutexLock mu (self,*Locks::runtime_shutdown_lock_);
runtime -> EndThreadBirth();
}
.
.
.
}
CreateCallback(void*arg) {
.
.
.
// Invoke the 'run' method of our java.lang.Thread.
ObjPtr<mirror::Object > receiver = self -> tlsPtr_.opeer;
//調用Thread的run方法(為什么不直接調用run()?)
jmethodID mid = WellKnownClasses::java_lang_Thread_run;
ScopedLocalRef<jobject> ref (soa.Env(), soa.AddLocalReference < jobject > (receiver));
InvokeVirtualOrInterfaceWithJValues(soa, ref.get(), mid, nullptr);
// Detach and delete self.
Runtime::Current () -> GetThreadList()->Unregister(self);
return nullptr;
}
run方法被那個調用?

為什么不直接調用run方法?(start()與run()的區(qū)別)
Java中start與run
1)Thread狀態(tài)來說
Start():新建狀態(tài)---就緒狀態(tài)
Run():新建狀態(tài)--運行狀態(tài)
2)能否新建線程來說
Start()可以新建線程。run()則不行
3)能否進行線程切換
start中的run代碼可以進行線程切換。
run方法必須等待其代碼全部執(zhí)行完才能繼續(xù)執(zhí)行。
線程安全性問題

public class NotSharingThread extends Thread{
private int count = 5;
public NotSharingThread(String threadName) {
super();
this.setName(threadName);
}
@Override
public void run() {
super.run();
while (count > 0){
count --;
System.out.println("由" + this.currentThread().getName()
+ "計算,count = " + count);
}
}
}
NotSharingThread a = new NotSharingThread("A");
NotSharingThread b = new NotSharingThread("B");
NotSharingThread c = new NotSharingThread("C");
a.start();
b.start();
c.start();
I/System.out: 由A計算,count = 4
I/System.out: 由A計算,count = 3
I/System.out: 由A計算,count = 2
I/System.out: 由A計算,count = 1
I/System.out: 由A計算,count = 0
I/System.out: 由B計算,count = 4
I/System.out: 由B計算,count = 3
I/System.out: 由B計算,count = 2
I/System.out: 由B計算,count = 1
I/System.out: 由B計算,count = 0
I/System.out: 由C計算,count = 4
I/System.out: 由C計算,count = 3
I/System.out: 由C計算,count = 2
I/System.out: 由C計算,count = 1
I/System.out: 由C計算,count = 0

public class LoginServlet {
private static String userNameRef;
private static String passwordRef;
public static void doPost(String userName,String password){
try{
userNameRef = userName;
if(userName.equals("a")) {
Thread.sleep(5000);
}
passwordRef = password;
System.out.println("userName = " + userNameRef +
"passWord = " + passwordRef);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class ALoginThread extends Thread{
@Override
public void run() {
LoginServlet.doPost("a","aa");
}
}
public class BLoginThread extends Thread{
@Override
public void run() {
LoginServlet.doPost("b","bb");
}
}
ALoginThread aLoginThread = new ALoginThread();
aLoginThread.start();
BLoginThread bLoginThread = new BLoginThread();
bLoginThread.start();
I/System.out: userName = b passWord = bb
I/System.out: userName = b passWord = aa
通過上述代碼我們發(fā)現(xiàn),同一時間多個線程對同一數據進行操作。引發(fā)了“非線程安全”。
通過synchronized方法可以避免“非線程安全”。
synchronized簡介
線程停止
Java中有三種方式退出正在運行的線程:
1)使用退出標志,使線程正常退出,當run方法完成后線程終止。
2)使用stop方法強行終止線程(不推薦)。
3)使用interrupt方法中斷線程(推薦)。
Thread.interrupt()
并不會終止一個正在運行的線程,還需要加入一個判斷才可以完成線程的停止。(interrupt()方法僅僅是在當前線程中打了一個停止的標記)
判斷線程是否是停止狀態(tài)
this.interrupted():測試當前線程是否已經是中斷狀態(tài)。執(zhí)行后具有將狀態(tài)標志清除為false的功能(前提是中斷狀態(tài))。
This.isInterrupted():測試線程Thread對象是否已經是中斷狀態(tài),但不清除狀態(tài)標志。
如何終止線程舉例(推薦使用try-catch)
public class InterruptedThread extends Thread{
@Override
public void run() {
super.run();
try{
for (int i = 0 ; i < 500000 ; i ++){
if(this.isInterrupted()) {//判斷是否是中斷標志
System.out.println("已經是停止狀態(tài)了!我要退出了");
throw new InterruptedException();
}
System.out.println("i = " + (i + 1));
}
System.out.println("我還在執(zhí)行");
}catch (InterruptedException e){
System.out.println("進入try-catch方法");
e.printStackTrace();
}
}
}
try{
InterruptedThread interruptedThread = new InterruptedThread();
interruptedThread.start();
Thread.sleep(5000);
//設置中斷的標記位
interruptedThread.interrupt();
}catch (InterruptedException exception){
System.out.println("主方法try-catch");
exception.printStackTrace();
}
輸出結果
I/System.out: i = 41998
I/System.out: 已經是停止狀態(tài)了!我要退出了
I/System.out: 進入try-catch方法
線程暫停(恢復)
暫停線程意味著此線程還可以恢復運行。suspend()暫停線程,resume()恢復線程。
線程優(yōu)先級(1-10)
線程優(yōu)先級具有繼承性。優(yōu)先級高的線程得到的CPU資源較多。但并不能保證優(yōu)先級高德線程先于優(yōu)先級低的線程執(zhí)行完任務。通過setPriority()可以設置優(yōu)先級。
常見方法
currentThread()
/**
* Returns a reference to the currently executing thread object.
*
* @return the currently executing thread.
*/
//得當當前正在執(zhí)行的線程
public static native Thread currentThread();
public class RunOrStartThread extends Thread {
public RunOrStartThread() {
System.out.println("構造方法打印:" + Thread.currentThread().getName());
}
@Override
public void run() {
System.out.println("run方法打印: " + Thread.currentThread().getName());
}
}
// 測試
RunOrStartThread runOrStartThread = new RunOrStartThread();
// runOrStartThread.start();
// I/System.out: 構造方法打?。簃ain
// I/System.out: run方法打印: Thread-142
// runOrStartThread.run();
// I/System.out: 構造方法打?。簃ain
// I/System.out: run方法打印: main
isAlive()
判斷當前線程是否處于活動狀態(tài)
sleep()
指定毫秒數內,暫停當前正在執(zhí)行的線程。
getId()
獲取線程的唯一標識。
yield方法
Yield()方法的作用是放棄當前CPU的資源,讓給其他線程。
Java中有兩種線程:
用戶線程和守護線程(具有陪護功能,典型的守護線程垃圾回收器)。當進程中不存在非守護線程了,守護線程自動銷毀。通過setDaemon()設置守護線程。
線程間通信
wait/notify
wait/notify:必須出現(xiàn)在同步方法或者同步代碼塊中;變量在特殊時刻需要特殊處理,避免CPU浪費。
wait:使當前線程進入預執(zhí)行隊列,直到接收到通知或者線程被中斷為止。*具有釋放鎖的操作。
notify:隨機恢復擁有同一對象鎖的wait線程。notify并不馬上釋放鎖直到synchronized代碼執(zhí)行完后才釋放。
使用管道流實現(xiàn)線程通信
public class WriteData {
public void writeMethod(PipedOutputStream out){
try{
Log.e("TAG", "write:");
for (int i = 0 ; i < 300 ; i++){
String outData = (i+1) + "";
out.write(outData.getBytes());
Log.e("TAG", "" + outData);
}
out.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
public class ReadData {
public void readMethod(PipedInputStream input) {
try{
Log.e("TAG", "read:");
byte[] bytes = new byte[1024];
int read = input.read(bytes);
while(read != -1) {
String s = new String(bytes, 0, read);
Log.e("TAG", "" + s);
read = input.read(bytes);
}
input.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
public class WriteThread extends Thread {
private WriteData data;
private PipedOutputStream outputStream;
public WriteThread(WriteData data, PipedOutputStream outputStream) {
this.data = data;
this.outputStream = outputStream;
}
@Override
public void run() {
super.run();
data.writeMethod(outputStream);
}
}
public class ReadThread extends Thread{
private ReadData readData;
private PipedInputStream inputStream;
public ReadThread(ReadData readData, PipedInputStream inputStream) {
this.readData = readData;
this.inputStream = inputStream;
}
@Override
public void run() {
super.run();
readData.readMethod(inputStream);
}
}
//熟悉流
try{
WriteData writeData = new WriteData();
ReadData readData = new ReadData();
PipedOutputStream pipedOutputStream = new PipedOutputStream();
PipedInputStream pipedInputStream = new PipedInputStream();
//使兩個Stream之間產生通信連接
pipedOutputStream.connect(pipedInputStream);
ReadThread readThread = new ReadThread(readData, pipedInputStream);
WriteThread writeThread = new WriteThread(writeData, pipedOutputStream);
writeThread.start();
Thread.sleep(500);
readThread.start();
}catch (IOException e){
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
join方法
等待當前線程銷毀。內部通過wait方法實現(xiàn)(線程終止時調用notifyAll方法)
public class MyThread extends Thread {
public volatile static int count;
private static void addCount(){
for (int i = 0 ; i < 100 ; i ++){
count ++;
}
Log.e("TAG", "count = " + count);
}
@Override
public void run() {
addCount();
}
}
MyThread myThread = new MyThread();
myThread.start();
Log.e("TAG", "1111111111111111111");
Log.e("TAG", "222222222222222222");
Log.e("TAG", "3333333333333333333333");
//TAG: 1111111111111111111
//TAG: 222222222222222222
//TAG: 3333333333333333333333
//TAG: count = 100
//加入join后(join具有釋放鎖的作用)
//TAG: count = 100
//TAG: 1111111111111111111
//TAG: 222222222222222222
//TAG: 3333333333333333333333