概述
Java虚拟机允许同时执行多个线程。每个线程都有优先级。高优先级的线程会优先执行。
守护线程和非守护线程
当Java虚拟机启动的时候,通常会有一个非守护线程,通常是main方法。Java虚拟机会持续执行,如果发生以下情况,java虚拟机会终止执行: 1)Runtime类的exit()方法被调用,并且安全管理器允许该操作。 2)所有非守护线程都执行结束,只剩下守护线程还在执行。
创建线程的2种方法
1)声明一个Thread类的子类,子类覆盖了Thread类的run()方法。然后创建一个该类的实例并执行start()方法。 2)创建一个类,实现runnable接口,并实现run()方法。创建该类的实例,并且执行start()方法。 因为Thread类实现Runnable接口。以上2种方法,个人认为貌似归根到底还是一种方法,那就是实现Runnable接口,并且实现run方法。
线程的标识
每个线程都有一个名称,用来识别线程。多个线程可以有相同的名称,新建线程的时候,如果没有指定名称,会自动生成一个名称。 以上是代码注释中说明的,这里就会有一个疑问了。名称相同,那虚拟机会不会把名称相同的线程混淆呢?答案是不会,因为在Thread类中有一个成员变量 private long tid; 这个才是Thread的唯一标识。
继承关系
public class Thread implements Runnable Thread 实现了Runnable接口
成员属性
- private volatile String name; // 线程名称
- private int priority; // 优先级
- private boolean daemon = false; // 是否守护线程
- private Runnable target; // target线程,也就是调用run()方法的主体。
- private ThreadGroup group; // 所处的线程组
- private ClassLoader contextClassLoader; //
- private AccessControlContext inheritedAccessControlContext; // 继承的访问控制上下文。
- ThreadLocal.ThreadLocalMap threadLocals = null; // 属于该线程的ThreadLocal变量,由ThreadLocal类维护。
- private long stackSize; // 对战大小
- private long tid; // 线程id
- private static long threadSeqNumber; // 用来生成tid
- private volatile int threadStatus = 0; // 线程状态
- public final static int MIN_PRIORITY = 1; // 最低的优先级
- public final static int NORM_PRIORITY = 5; // 默认的优先级
- public final static int MAX_PRIORITY = 10; // 最高的优先级
- public enum State
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
构造器
主要的构造器
主要构造器有4个变量 ThreadGroup group 线程组 Runnable target 目标:谁的run()方法被调用,导致该线程被执行,指的就是谁。 String name 线程名称 long stackSize 线程的栈的大小。也就是jvm虚拟机栈的概念。虚拟机栈在jvm中,是线程独享的。
public Thread(ThreadGroup group, Runnable target, String name, long stackSize) {
init(group, target, name, stackSize);
}
init方法
从代码中可以看到,init方法才是实际上做了很多“构造”工作的方法。但是其实init还有另外的2个参数,
AccessControlContext acc 访问权限上下文 boolean inheritThreadLocals 如果值为true,就会继承构建该线程的线程的thread-local变量。
我们看init方法的代码源码
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();
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();
}
重载的构造方法
另外,还有一个重载构造方法:
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
public Thread(String name) {
init(null, null, name, 0);
}
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
关键方法
currentThread()
static方法,返回当前正在执行的线程。
public static native Thread currentThread();
yield()
static方法,表示当前线程愿意让出占据的处理器,但是调度器可以自由忽略该操作。
public static native void yield();
sleep()
使当前线程休眠指定的毫秒数,取决于系统计时器和调度程序的精度和准确性。线程不会失去任何监视器的所有权。
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos)
throws InterruptedException
start()
调用该方法会导致线程开始执行。JVM会自动调用该线程的run()方法。 start()方法不能被多次调用。
public synchronized void start() {}
run()
执行target的run()方法。
@Override public void run() {
if (target != null) {
target.run();
}
}
stop()
强制终止执行线程。 没有被start()的线程,也可以执行stop()方法。 1)会导致立刻释放所有的锁。 2)即刻抛出ThreadDeath异常,在线程的run()方法内,任何一点都有可能抛出ThreadDeath Error,包括在catch或finally语句中。 这种方法本质上是不安全的,会引起数据一致性的问题。
@Deprecated public final void stop()
interrupt()
中断此线程。除非始终允许当前线程中断自身,否则将调用此线程的 checkAccess 方法,这可能会引发SecurityException。 如果因为调用wait(),join(),sleep() 方法而处于blocked状态,interrupt()会抛出InterruptedException异常。 如果线程正在进行IO操作,channel会关闭,并且抛出 ClosedByInterruptException 异常。 如果线程阻塞在一个java.nio.channels.Selector里,那么线程会被设置为中断状态,并且会立即返回一个非0的值。就像Selector的wakeup方法被调用。 中断一个非活动的线程,是没有任何作用的。
public void interrupt()
interrupted()
测试当前线程是否已被中断。通过此方法可以清除线程的中断状态。换句话说,如果要连续两次调用此方法,则第二个调用将返回false
public static boolean interrupted()
isInterrupted()
判断线程是否被中断,并清除中断状态,也就是说,被中断的线程,第一次调用的时候,会返回true,第二次会返回false。
public boolean isInterrupted()
destroy()
此方法最初设计为在不进行任何清理的情况下破坏此线程。它所持有的任何监视器都将保持锁定状态。但是,该方法从未实现。如果要实施,则将很容易发生suspend的死锁。如果目标线程在销毁关键系统资源时持有锁来保护它,则任何线程都无法再次访问该资源。如果另一个线程曾试图锁定此资源,则将导致死锁。这种僵局通常表现为“冻结”进程。 源码中,该方法已经被废弃,现在只是抛出了一个异常。
@Deprecated
public void destroy() {
throw new NoSuchMethodError();
}
isAlive()
判断当前线程是否处于活动状态。活跃状态指的是启动了,但是没有被终止。
public final native boolean isAlive();
suspend()
挂起线程,和resume()方法配合使用。 天生容易死锁,如果先调用了resume(),再调用suspend(),那么就会发生死锁。
@Deprecated public final void suspend()
resume()
恢复线程,和suspend()方法配合使用。该方法已经废弃。 如果先调用了resume(),再调用suspend(),那么就会发生死锁。
@Deprecated public final void resume()
setPriority()
设置线程优先级
public final void setPriority(int newPriority)
getPriority()
获取线程优先级
public final int getPriority()
setName()
设置线程名称
public final synchronized void setName(String name)
getName()
获得线程名称
public final String getName()
getThreadGroup()
获取线程组。
public final ThreadGroup getThreadGroup()
join()
执行目标线程一定时间,时间到,或者目标线程执行完毕,则返回。
public final synchronized void join(long millis, int nanos) throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException
public final void join() throws InterruptedException
setDaemon()
设置为守护线程。
public final void setDaemon(boolean on)
holdsLock()
判断当前线程是否持有指定的锁。
public static native boolean holdsLock(Object obj);
getId()
获取线程id
public long getId()
getState()
获取线程状态。
public State getState()
要点分析
start() run()方法的区别?
调用线程的start方法是创建了新的线程,在新的线程中执行。 调用线程的run方法是在主线程中执行该方法,和调用普通方法一样。
suspend() && resume() 为何被废弃
容易导致死锁,最显著的问题就是先调用resume()后调用suspend()方法后,就会造成死锁。
线程有几种状态,分别是如何转化的。
NEW: 新创建的线程,没有被start()的线程。处于该状态。
RUNNABLE: 可运行的线程具有该状态,线程处于runnable状态,表示正在jvm中运行,但是也有可能会正在等待操作系统资源,例如处理器资源。
BLOCKED: 表示线程处于阻塞状态,在等待监视器的锁。处于该状态的线程,通常是调用了Object.wait()方法,正在等待监视器锁,获得锁以后进入或者重新进入一个同步代码块或者同步方法。
WAITING: 可以通过以下方法进入该状态: Object.wait() Thread.join() LockSupport.park() 一个处于该状态的线程,表示正在等待另一个线程做相应的操作。 比如一个调用了Object.wait()方法的线程,正在等待另一个线程调用notify()或notifyAll()操作。
TIMED_WAITING: 具有指定等待时间的等待线程的线程状态。 由于使用以下指定的正等待时间调用以下方法之一,线程处于定时等待状态: Thread.sleep Object.wait Thread.join LockSupport.parkNanos LockSupport.parkUntil
TERMINATED: 表示线程已经完成执行,并且已经终止。
希望和大家多多交流
我16年毕业以后,做的是前端,目前打算深入学习java开发。内容有任何问题,欢迎各位小伙伴们指正,也希望小伙伴们给我点赞和关注,给我留言,一起交流讨论,共同进步。