Thread类学习(一)

Thread类API中的英文描述:

file:///D:/Java/JDK8/Docs/docs/api/index.html

英语不好,大致的意思是:

线程是程序执行时的线程,java虚拟机(JVM)允许一个应用运行多个线程(并发)。

每一个线程都自己的优先权,优先级高的线程会比优先级低的线程优先执行。每一个线程可以被设置成守护线程(daemon),当一段代码运行一些线程时,会创建一个Thread类的对象,这个新的线程初始的优先级是创建这个线程对象时程序员设定好的,仅且仅当用户把这个线程设定成daemon,这个线程才是daemon

当java虚拟机(JVM)开启时,这儿通常有一个非daemon线程(一般的会调用某个类的main方法),java虚拟机(JVM)会一直执行这些线程,直到出现下面几种情况:

第一种:程序调用Runtime类的退出方法,并且安全管理器允许该退出操作

第二种:除了daemon线程外的其他所有线程执行完毕(处于dead状态);线程run方法中遇到return语句;线程抛出一个run方法无法处理的异常

有两种方法来创建一个新的线程。

第一种:声明一个类,该类继承Thread,该类应该重写Thread类的run方法,通过调用start方法来启动该线程,下面给出一个列子:

 1 class PrimeThread extends Thread {
 2   long minPrime;
 3   PrimeThread(long minPrime) {
 4     this.minPrime = minPrime;
 5   }
 6 
 7   public void run() {
 8     // compute primes larger than minPrime
 9     . . .
10   }
11 }

 

第二种:声明一个类,实现Runnable接口,重写Runnable接口中的run方法,下面也给出一个例子:

 1 class PrimeRun implements Runnable {
 2     long minPrime;
 3     PrimeRun(long minPrime) {
 4         this.minPrime = minPrime;
 5     }
 6     public void run() {
 7         // compute primes larger than minPrime
 8         . . .
 9     }
10 }                

下面我们来看一下Thread的源代码部分:

1. Thread类实现了Runnable接口:

public class Thread implements Runnable

2. Thread类的构造器:

1 public Thread()
2 public Thread(Runnable target)
3 Thread(Runnable target, AccessControlContext acc)
4 public Thread(ThreadGroup group, Runnable target)
5 public Thread(String name)
6 public Thread(ThreadGroup group, String name)
7 public Thread(Runnable target, String name)
8 public Thread(ThreadGroup group, Runnable target, String name)
9 public Thread(ThreadGroup group, Runnable target, String name, long stackSize)

Thread类一共有9个构造器。其中第3个构造器没有public修饰,默认用default修饰,同一个包下可用,不多做说明。

通过上面9个构造器(除3)可以看出,用户在创建一个Thread类的对象时,可以设定的参数有:ThreadGroup、Runnable、name、stackSize

ThreadGroup:是java.lang包下的一个ThreadGroup类,ThreadGroup对象表示一个线程的集合,也可以包含另一个ThreadGroup对象。

Thread类init方法关于ThreadGroup部分源码:

 1         Thread parent = currentThread();
 2         SecurityManager security = System.getSecurityManager();
 3         if (g == null) {
 4             /* Determine if it's an applet or not */
 5 
 6             /* If there is a security manager, ask the security     
 7             manager what to do. */
 8             if (security != null) {
 9                 g = security.getThreadGroup();
10             }
11 
12             /* If the security doesn't have a strong opinion of the 
13             matter use the parent thread group. */
14             if (g == null) {
15                 g = parent.getThreadGroup();
16             }
17         }
18 
19         /* checkAccess regardless of whether or not threadgroup 
20         is explicitly passed in. */
21         g.checkAccess();

其中currentThread()方法是获取当前运行的线程,下面写段代码做个试验:

1 public class Demo7{
2     public static void main(String[] args){
3         Thread t = Thread.currentThread();
4         System.out.println(t.getName());
5     }
6 }

System.getSecurityManager()是System类中的一个静态方法,该方法是用来返回一个SecurityManager类的对象security,下面是System类中getSecurityManager():

    /*** Gets the system security interface.** @return  if a security manager has already been established for the*          current application, then that security manager is returned;*          otherwise, <code>null</code> is returned.* @see     #setSecurityManager*/public static SecurityManager getSecurityManager() {return security;}

注释说明,如果这个安全管理已经创建security对象,则返回这个security,如果没有,则返回null,其中System类的security初始值被设置为null。

    /* The security manager for the system.*/private static volatile SecurityManager security = null;

再看Thread类中init方法关于ThreadGroup部分做了什么处理?

先创建两个对象parent和security:

Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();

接着对用户创建Thread类对象设置的参数做一个判断,如果用户没有设定ThreadGroup参数,则传递一个null值

init方法先判断这个参数是否为null,如果不为null,则判断security是否为空,如果不为空,则获取security所在的ThreadGroup赋值给用户创建的Thread对象,即g的值

接着再判断g是否为空,如果还为null,则将当前线程的ThreadGroup赋值给g。总之,如果用户未设置g值,就把security的g值赋值给g,如果security的g值也为空,就把parent的g值赋给g。

最后再调用g(ThreadGroup)的checkAccess方法,ThreadGroup类的checkAccess方法源码:

1     public final void checkAccess() {
2         SecurityManager security = System.getSecurityManager();
3         if (security != null) {
4             security.checkAccess(this);
5         }
6     }

可以看出该方法,其实是调用了SecurityManager对象的checkAccess(ThreadGroup g)方法:

 1     public void checkAccess(ThreadGroup g) {
 2         if (g == null) {
 3             throw new NullPointerException("thread group can't be null");
 4         }
 5         if (g == rootGroup) {
 6             checkPermission(SecurityConstants.MODIFY_THREADGROUP_PERMISSION);
 7         } else {
 8             // just return
 9         }
10     }

其中rootGroup应该是g的最高级parent(未必正确),看一下源码:

1 private static ThreadGroup rootGroup = getRootGroup();
2 private static ThreadGroup getRootGroup() {
3         ThreadGroup root =  Thread.currentThread().getThreadGroup();
4         while (root.getParent() != null) {
5             root = root.getParent();
6         }
7         return root;
8 }

checkPermission(SecurityConstants.MODIFY_THREADGROUP_PERMISSION)这个坑之后再填吧,挖不下去了。

Runnable:前面介绍了创建Thread类对象的两种方法,其中一种就是传入一个Runnable对象。

如果在创建一个线程时,没有传入Runnable对象,则init方法默认传入的是一个null值,来看一下源码:

public Thread() {init(null, null, "Thread-" + nextThreadNum(), 0);
}

 

在看看init方法对应参数代表什么意思:

private void init(ThreadGroup g, Runnable target, String name, long stackSize) {init(g, target, name, stackSize, null, true);
}

 

从中还可以看出,如果在创建Thread类对象时,没有指定其姓名,会默认设置名字,即Thread-加上nextThreadNum()返回值:

private static int threadInitNumber;
private static synchronized int nextThreadNum() {return threadInitNumber++;
}

 

所以第一个线程的名字会默认为Thread-0,第二个线程的名字为Thread-1......

stackSize:栈的大小,待补充......

3. 线程的状态

Thread类有个内部枚举类State,该类就声明了线程的几种状态:

public enum State {NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;
}

 

NEW:线程刚创建时具有的状态;

public class Demo8{public static void main(String[] args){Thread t = new Thread();System.out.println(t.getState());}
}

 

RUNNABLE:是指线程正在运行,线程获取CPU的时间片

public class Demo8{public static void main(String[] args){Thread t = new Thread();t.start();System.out.println(t.getState());}
}

 

BLOCKED:是指程序进入阻塞状态,假如有两个线程,并且两个线程都是同步安全的(synchronized),当一个线程处于runnable状态时,则另一个线程处于blocked状态。

public class Demo8{public static void main(String[] args){Thread t1= new Thread(){public void run(){synchronized(Thread.class){for(int i = 0; i < 100; i++){}}}};Thread t2 = new Thread(){public void run(){synchronized(Thread.class){System.out.println(t1.getState());}}};t1.setPriority(1);t2.setPriority(10);t1.start();t2.start();}
}

 

WAITING:程序处于等待状态,调用wait()、join()、await()、lock()等方法,都会使线程处于waiting状态,需要注意的是这些方法必须是无参数的。

public class Demo8{public static void main(String[] args){Thread t1 = new Thread(){public void run(){try{join();    }catch(InterruptedException e){e.printStackTrace();}}};Thread t2 = new Thread(){public void run(){System.out.println(t1.getState());}};t1.start();t2.start();}
}

 

TIMED_WAITING:程序处于限时等待状态,调用wait()、join()、await()、lock()、sleep()等方法,都会使线程处于waiting状态,需要注意的是这些方法必须加入参数。

TERMINATED:终止状态,即线程结束

4. Thread类中部分方法源码解析

————this.start()方法

public synchronized void start() {if (threadStatus != 0)throw new IllegalThreadStateException();group.add(this);boolean started = false;try {start0();started = true;} finally {try {if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {}}
}

 

可以看出线程是在调用start方法时加入init方法中指定的线程池的,其次线程在创建时并不是把内部的枚举类State的NEW值给这个线程,而是定义一个int型的threadStatus

变量,并且这个变量初始值为0。

private volatile int threadStatus = 0;

 

并且start还调用了一个本地的start0()方法,由于没看过JVM相关的知识,所以对于native修饰的方法无能无力:

private native void start0();

 

不过IBM有一篇文章对此讲解的比较详细,这里附上链接:https://www.ibm.com/developerworks/cn/java/j-lo-processthread/#icomments

大概的意思是java的start方法会调用 JVM_StartThread方法,而 JVM_StartThread方法会创建一个与本地相关的线程,该线程与java创建的线程有着一一对应关系。

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) …native_thread = new JavaThread(&thread_entry, sz); …

 

这篇文章并没有详细讲解JVM的代码实现过程,而是给出了方法调用图,该图来自上面链接的那篇文章:

————setPriority(int)方法

这里先留坑,简单讲一下,线程的优先级可以理解为线程抢占cpu时间片的概率,因此并不能保证优先级高一定会先执行

 1 public class Demo2 {
 2     public static void main(String[] args) {
 3         Thread t1 = new Thread() {
 4             @Override
 5             public void run() {
 6                 for(int i = 0; i < 1000; i++) {
 7                     System.out.println("********");
 8                 }
 9             }
10         };
11         Thread t2 = new Thread() {
12             @Override
13             public void run() {
14                 for(int i = 0; i < 1000; i++) {
15                     System.out.println("--------");
16                 }
17             }
18         };
19         t1.setPriority(1);
20         t2.setPriority(10);
21         t1.start();
22         t2.start();
23     }
24 }

 

其次,windows操作系统的优先级为7个级别(未验证),而java的优先级分为10个级别,所以java程序1~10几个优先级中,必定有几个优先级在windows操作系统下级别是一样的,真正决定优先级的应该是本地的setPriority0()方法。

setPriority0(priority = newPriority);

 

—————activeCount方法,获取当前线程池中的线程数量

 1 import java.lang.reflect.InvocationTargetException;
 2 import java.lang.reflect.Method;
 3 public class Demo3 {
 4     public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 5         MyRunnable m = new MyRunnable();
 6         Thread t1 = Thread.currentThread();
 7         ThreadGroup tg = t1.getThreadGroup();
 8         Thread t2 = new Thread(m);
 9         Class c = ThreadGroup.class;
10         Method declaredMethod = c.getDeclaredMethod("add", Thread.class);
11         Method method = declaredMethod;
12         method.setAccessible(true);
13         method.invoke(tg,t2);
14         System.out.println(Thread.activeCount());
15         System.out.println(t1.getName());
16         System.out.println(tg.getName());
17     }
18 }
19 class MyRunnable implements Runnable{
20     public void run() {}
21 }

这里是利用反射的机制,调用ThreadGroup类中的add(Thread t)方法,把线程添加到指定的线程池,这里要说明的是,创建的线程对象时,Thread类构造器调用的init初始化方法,并没有把线程加入指定的线程池,而是在start方法中调用了ThreadGroup的add方法。

未完待续......

 

转载于:https://www.cnblogs.com/kirito2924/p/9147630.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/427633.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

做企业网站为什么要服务器呢,企业为什么要建站?

一、企业建站为何要建立自己的网站1、企业建站的第1个好处就是树立企业形象酒香不怕巷子深&#xff0c;说的就是线下企业经营时以自身企业形象带来的效果&#xff0c;但企业在网上经营活动时&#xff0c;一方面没有大量老客户支援&#xff0c;另一方面陌生人也没有信任感&#…

[转]Oh My Zsh,安装,主题配置

https://swp-song.com/2017/08/20/Tools/OhMyZsh%E5%AE%89%E8%A3%85%E5%92%8C%E4%B8%BB%E9%A2%98%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95/ Oh MyZsh 是什么 Oh My Zsh 它是基于 zsh 命令行的一个扩展工具集&#xff0c;驱动的命令行工具&#xff0c;提供了主题配置 / 插件机制 /…

Linux系统运维成长记

2017年秋天&#xff0c;大一入学&#xff0c;由于高考分数没有达标计算机专业于是被调剂到了土木工程专业。起初大家一起上课下课感觉不到什么变化&#xff0c;但是随着时间的流逝&#xff0c;日子久了&#xff0c;我越发觉得这样的生活毫无乐趣。我的主要兴趣在计算机方面&…

手把手教你撸一个简易的 webpack

背景 随着前端复杂度的不断提升&#xff0c;诞生出很多打包工具&#xff0c;比如最先的grunt&#xff0c;gulp。到后来的webpack和Parcel。但是目前很多脚手架工具&#xff0c;比如vue-cli已经帮我们集成了一些构建工具的使用。有的时候我们可能并不知道其内部的实现原理。其实…

centos7.3下apache搭建django[未成功]

centos7.3下apache搭建django[未成功] 1 apache肯定已经按照完毕了&#xff0c; 如果没有 yum install httpd yum install mod_wsgi 安装完成之后&#xff0c;mod_wsgi.so会在Apache的modules目录中, 2 在httpd.conf文件中添加以下内容  LoadModule wsgi_module modules/mod…

排序算法入门之冒泡排序

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01;在开发中&#xff0c;对一组数据进行有序地排…

VMware12.0下安装CentOS-6.9-x86_64-bin-DVD.iso

使用的是vmware workstation 12 pro 创建虚拟机 注意上面的 安装程序光盘镜象文件&#xff08;iso&#xff09;(M): 是我之前配置&#xff0c;现在可以不做任何处理 此处使用的是centos的64位 在创建虚拟机存在的地方是D:\vmware\redis&#xff0c;因为我准备在这装redis集群&a…

判断回文字符串

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01;《递归入门》所谓回文字符串&#xff0c;就是…

matplotlib 中文显示 的问题

第一种方法 from pylab import mpl import numpy as npmpl.rcParams[font.sans-serif] [SimHei] # 指定默认字体 mpl.rcParams[axes.unicode_minus] False # 解决保存图像是负号 - 显示为方块的问题 %pylab inline t np.arange(-5 * np.pi, 5 * np.pi, 0.01) y np.sin…

排序算法入门之冒泡排序优化

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01;这篇文章是对上一篇文章中的冒泡排序进行优化…

递归入门 斐波那契数列

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01;《递归入门》斐波那契数列百度百科斐波那契数…

用栈解决四则运算问题

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01;本文章的解决方法参考了《大话数据结构》中关…

源码篇:Python 实战案例----银行系统

import time import random import pickle import osclass Card(object):def __init__(self, cardId, cardPasswd, cardMoney):self.cardId cardIdself.cardPasswd cardPasswdself.cardMony cardMoneyself.cardLock False # 后面到了锁卡的时候需要有个卡的状态class User…

排序算法入门之简单选择排序

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01;在学了冒泡排序后&#xff0c;会发觉这种算法…

ubuntu12 04下django安装略谈

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01;首先你需要肯定你的机子上装了python现在ubu…

React Native 开发环境搭建

1、安装 Python 2&#xff0c;不知道是否已支持 Python 3 2、安装 node&#xff0c;npm。。。 修改 npm 镜像&#xff0c;不建议使用 cnpm&#xff0c;cnpm 安装模块的路径与 npm 有差别 npm config set registry https://registry.npm.taobao.org --global npm config set dis…

递归入门

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01;写在前面&#xff1a;对于强大的递归。要想做…

判断一个数是偶数还是奇数

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01;《递归入门》交互递归到目前为止&#xff0c…

C语言的fgets 与 gets

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01;今天在翻《C语言参考手册》查看文件操作的相…

递归入门 阶乘函数

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01;《递归入门》对许多人而言&#xff0c;理解递…