Java多线程-基础篇
学习消息机制后,有几个关于线程的问题。于是有了这篇文章。本文绝大多数是对于 Java多线程编程核心技术的总结
什么是线程
执行程序中的一个线程,Java虚拟机允许应用程序同时运行多个执行线程。每个线程都有优先权。具有较高优先级的线程优先于具有较低优先级的线程执行。每个线程可能会被标记为守护进程。当在某个线程中运行的代码创建一个新Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护进程线程。
线程和进程的关系
一个进程中可以包含多个线程Thread的状态(有待补充)
Thread状态线程的启动
如何实现Thread
* public class Thread implements Runnable
* public interface Runnable
继承Thread或者实现Runnable接口
public interface Runnable {
public abstract void run();
}
我们来看一下Runnable实现类Thread中的run方法。
@Override
public void run() {
if (target != null) {//target是如何初始化?
target.run();
}
}
//线程初始化
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
//currentThread():返回当前正在执行线程的引用
Thread parent = currentThread();
if (g == null) {
g = parent.getThreadGroup();//线程终止返回null,否则返回ThreadGroup对象
}
g.addUnstarted();//记录未启动的线程
this.group = g;
this.target = target;
//线程的优先级具有继承关系
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()方法怎么执行?
init(null, null, "Thread-" + nextThreadNum(), 0);
}
通过上面的分析我们知道了Thread初始化执行了那些操作。但是run方法是怎么被执行的?
我们看一下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()的区别)
Java中start与run
1)Thread状态来说
Start():新建状态---就绪状态
Run():新建状态--运行状态
2)能否新建线程来说
Start()可以新建线程。run()则不行
3)能否进行线程切换
start中的run代码可以进行线程切换。
run方法必须等待其代码全部执行完才能继续执行。
线程安全性问题
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
通过上述代码我们发现,同一时间多个线程对同一数据进行操作。引发了“非线程安全”。
通过synchronized方法可以避免“非线程安全”。
synchronized简介
线程停止
Java中有三种方式退出正在运行的线程:
1)使用退出标志,使线程正常退出,当run方法完成后线程终止。
2)使用stop方法强行终止线程(不推荐)。
3)使用interrupt方法中断线程(推荐)。
Thread.interrupt()
并不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。(interrupt()方法仅仅是在当前线程中打了一个停止的标记)
判断线程是否是停止状态
this.interrupted():测试当前线程是否已经是中断状态。执行后具有将状态标志清除为false的功能(前提是中断状态)。
This.isInterrupted():测试线程Thread对象是否已经是中断状态,但不清除状态标志。
如何终止线程举例(推荐使用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("已经是停止状态了!我要退出了");
throw new InterruptedException();
}
System.out.println("i = " + (i + 1));
}
System.out.println("我还在执行");
}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: 已经是停止状态了!我要退出了
I/System.out: 进入try-catch方法
线程暂停(恢复)
暂停线程意味着此线程还可以恢复运行。suspend()暂停线程,resume()恢复线程。
线程优先级(1-10)
线程优先级具有继承性。优先级高的线程得到的CPU资源较多。但并不能保证优先级高德线程先于优先级低的线程执行完任务。通过setPriority()可以设置优先级。
常见方法
currentThread()
/**
* Returns a reference to the currently executing thread object.
*
* @return the currently executing thread.
*/
//得当当前正在执行的线程
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: 构造方法打印:main
// I/System.out: run方法打印: Thread-142
// runOrStartThread.run();
// I/System.out: 构造方法打印:main
// I/System.out: run方法打印: main
isAlive()
判断当前线程是否处于活动状态
sleep()
指定毫秒数内,暂停当前正在执行的线程。
getId()
获取线程的唯一标识。
yield方法
Yield()方法的作用是放弃当前CPU的资源,让给其他线程。
Java中有两种线程:
用户线程和守护线程(具有陪护功能,典型的守护线程垃圾回收器)。当进程中不存在非守护线程了,守护线程自动销毁。通过setDaemon()设置守护线程。
线程间通信
wait/notify
wait/notify:必须出现在同步方法或者同步代码块中;变量在特殊时刻需要特殊处理,避免CPU浪费。
wait:使当前线程进入预执行队列,直到接收到通知或者线程被中断为止。*具有释放锁的操作。
notify:随机恢复拥有同一对象锁的wait线程。notify并不马上释放锁直到synchronized代码执行完后才释放。
使用管道流实现线程通信
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方法实现(线程终止时调用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