线程和进程的区别总结:
- 进程是一段正在执行的程序,是资源分配的基本单元,而线程是CPU调度的基本单元。
- 进程间相互独立、不能共享资源,一个进程至少有一个线程,同一进程的各线程共享整个进程的资源(寄存器、堆栈、上下文)。
- 线程的创建和切换开销比进程小。
public class Thread extends Object implements Runnable
线程是程序中执行的线程。 Java虚拟机允许应用程序同时执行多个执行线程。
每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。 当在一些线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。
创建一个新的执行线程有两种方法:继承Thread类和实现Runnable接口。
//创建Thread对象并重写run方法
new Thread() {
@Override
public void run() {
System.out.println("第一个线程执行...");
}
}.start();
//创建Thread对象并传入一个实现Runnable接口的匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("第二个线程执行...");
}
}).start();
执行结果:

线程状态
线程状态 Thread.State。 线程可以处于以下状态之一:
NEW
线程尚未启动的线程状态。线程创建未调用start方法前,就为NEW状态。
RUNNABLE
可运行线程的线程状态。 可运行状态的线程正在Java虚拟机中执行,但它可能正在等待来自操作系统(例如处理器)的其他资源。
BLOCKED
一个线程的线程状态阻塞等待监视器锁定。 处于阻塞状态的线程正在等待监视器锁定进入同步块/方法,或者在调用Object.wait后重新输入同步的块/方法。
WAITING
等待线程的线程状态 由于调用以下方法之一,线程处于等待状态:
-
不带超时值的 Object.wait
-
不带超时值的 Thread.join
-
LockSupport.park
处于等待状态的线程正等待另一个线程,以执行特定操作。 例如,已经在某一对象上调用了 Object.wait() 的线程正等待另一个线程,以便在该对象上调用 Object.notify() 或 Object.notifyAll()。已经调用了 Thread.join() 的线程正在等待指定线程终止。
TIMED_WAITING
具有指定等待时间的等待线程的线程状态。 线程处于定时等待状态,因为在指定的正等待时间内调用以下方法之一:
- Thread.sleep
- 带有超时值的 Object.wait
- 带有超时值的 Thread.join
- LockSupport.parkNanos
- LockSupport.parkUntil
TERMINATED
终止线程的线程状态。 线程已完成执行。

常用方法
public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
Thread t = new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
t.start();
运行结果:

public void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。Thread 的子类应该重写该方法。
Thread t = new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
t.run();
运行结果:

通过去start()方法对比发现,调用start方法打印出的当前线程名为 Thread-0 (即线程 t);而调用run方法打印出的当前线程确实main。结合网上查找的资料,得出结论:调用start方法为启动线程,该线程执行自身的run方法;调用run方法为在当前线程(此处为main线程)中执行run方法,并没有启动该run方法所属的线程。直接调用run方法违背了创建线程的初衷。
public long getId()
返回该线程的标识符。线程 ID 是一个正的 long 数,在创建该线程时生成。线程 ID 是惟一的,并终生不变。线程终止时,该线程 ID 可以被重新使用。
public final String getName()
返回该线程的名称。
public final int getPriority()
返回线程的优先级。
public Thread.State getState()
返回该线程的状态。 该方法用于监视系统状态,不用于同步控制。
public final ThreadGroup getThreadGroup()
返回该线程所属的线程组。 如果该线程已经终止(停止运行),该方法则返回 null。
public final boolean isAlive()
测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。
public final void setName(String name)
改变线程名称,使之与参数 name 相同。
name - 该线程的新名称。
首先调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException。
public final void setPriority(int newPriority)
更改线程的优先级。
newPriority - 要为线程设定的优先级
首先调用线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException。
在其他情况下,线程优先级被设定为指定的 newPriority 和该线程的线程组的最大允许优先级相比较小的一个。
public static Thread currentThread()
返回对当前正在执行的线程对象的引用。
可配合着其他方法一起使用,如:
new Thread() {
@Override
public void run() {
System.out.println("获取线程名:" + Thread.currentThread().getName());
System.out.println("获取Id:" + Thread.currentThread().getId());
System.out.println("获取优先级:" + Thread.currentThread().getPriority());
System.out.println("获取线程状态:" + Thread.currentThread().getState());
}
}.start();
执行结果如下:

public void interrupt()
中断线程。所谓的中断线程,其实只是在该线程上加了一个中断标志,并不会使该线程停止或暂停执行。被中断的线程还是会继续执行,这时可以通过调用interrupted或isInterrupted方法对该线程进行需要的操作处理。
如果当前线程没有中断它自己(这在任何情况下都是允许的),则该线程的 checkAccess 方法就会被调用,这可能抛出 SecurityException。
如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。
如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException。
如果该线程在一个 Selector 中受阻,则该线程的中断状态将被设置,它将立即从选择操作返回,并可能带有一个非零值,就好像调用了选择器的 wakeup 方法一样。
如果以前的条件都没有保存,则该线程的中断状态将被设置。
public static boolean interrupted()
如果当前线程已经中断,则返回 true;否则返回 false。
测试当前线程是否已经中断。线程的中断状态由该方法清除(该方法会清除该线程的中断状态,即撤销该线程的中断标志,成为未中断状态)。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。
public boolean isInterrupted()
如果该线程已经中断,则返回 true;否则返回 false。
测试线程是否已经中断。线程的中断状态 不受该方法的影响(不会清除该线程的中断状态)。
public void run() {
//线程并未中断
System.out.println("isInterrupted:" + Thread.currentThread().isInterrupted());
//中断线程
Thread.currentThread().interrupt();
//线程中断后并不会死亡,此时线程状态为RUNNABLE
System.out.println("getState:" + Thread.currentThread().getState());
//线程中断后isInterrupted为true
System.out.println("isInterrupted:" + Thread.currentThread().isInterrupted());
//此时线程已经中断,调用interrupted返回true 并且清除线程的中断状态
System.out.println("interrupted:" + Thread.interrupted());
//线程中断状态已经被interrupted方法清除,所以此时isInterrupted为false
System.out.println("isInterrupted:" + Thread.currentThread().isInterrupted());
//线程中断状态已被清除,调用interrupted返回false 并且清除线程的中断状态
System.out.println("interrupted:" + Thread.interrupted());
//线程中断状态已经被interrupted方法清除,所以此时isInterrupted为false
System.out.println("isInterrupted:" + Thread.currentThread().isInterrupted());
}
该方法执行结果如下:

在网上找线程中断的资料时,发现了一篇写的很详细的博客,在这里推荐一下:
https://www.cnblogs.com/yangming1996/p/7612653.html
public final void join()
等待该线程终止。
public final void join(long millis)
等待该线程终止的时间最长为 millis 毫秒。超时为 0 意味着要一直等下去。
public final void join(long millis, int nanos)
等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
join函数一般用于线程需要一定顺序执行的情况。在一个线程中调用另一个线程的join函数(如t.join),表示这个线程需要等待另一个线程执行结束后才继续执行。
static int count = 10;
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
while(count > 5) {
System.out.println(Thread.currentThread().getName() + "正在执行");
count --;
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
while(count > 0) {
System.out.println(Thread.currentThread().getName() + "正在执行");
count --;
}
}
};
System.out.println("main线程执行,准备执行t1");
t1.start();
//main线程需等待t1执行完毕才可继续执行
t1.join();
System.out.println("main线程继续执行,准备执行t2");
t2.start();
//main线程需等待t2执行完毕才可继续执行
t2.join();
System.out.println("main线程执行结束");
}
程序运行结果如下:

如将程序中的t1.join()和t2.join()注释掉,main线程就不会等待t1和t2先执行完毕,运行结果就会不一样。如下:

public static void sleep(long millis)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。该线程不丢失任何监视器的所属权。
public static void sleep(long millis,int nanos)
在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行)。该线程不丢失任何监视器的所属权。
线程sleep不会释放资源。线程若在拿到锁资源之后进入了休眠,其它等待该锁资源的线程也需等待,因为该锁资源并没有被释放。
static int count = 0;
public static void main(String[] args) throws Exception {
new Thread() {
@Override
public void run() {
while(true) {
synchronized ("锁对象") {
System.out.println(Thread.currentThread().getName() + "开始计时:" + count + "s");
count ++;
try {
//线程休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}.start();
new Thread() {
@Override
public void run() {
while(true) {
synchronized ("锁对象") {
System.out.println(Thread.currentThread().getName() + "开始计时:" + count + "s");
count ++;
try {
//线程休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}.start();
}
上述程序中,两个线程使用了锁对象为字符串常量 :”锁对象” ,两个线程同步打印计时并休眠1秒。运行结果如下,其中该结果中每行打印时间间隔1秒,并没有因为t1休眠,而t2立即抢到资源打印的情况。

在线程的run方法中,sleep()方法需要使用try-catch包裹,即使main方法上已声明异常抛出;若main方法上已声明异常抛出,main方法中调用sleep()方法时,则不用try-catch包裹。
public static void yield()
API中对该方法的解释为:暂停当前正在执行的线程对象,并执行其他线程。
yiled方法和sleep方法都不释放锁资源,在这里并没有写出合适的程序证明。
这样很容易让人产生误解,觉得调用这个方法就是暂停当前线程,让其他线程执行,这样的理解其实不对的。yield 使当前线程让出 CPU 时间片,线程从运行状态(Running)变为可执行状态(Runnable),处于可执行状态的线程有可能会再次获取到时间片继续执行,也有可能处于等待状态,直到再次获取到时间片。也就是说,后续会有两种情况:
- 当前线程让出 CPU 时间片后,又立即获取到 CPU 时间片,进而继续执行当前方法。
- 当前线程让出 CPU 时间片后,等待一段时间后获取到 CPU 时间片,进而继续执行当前方法。
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
while (count < 10) {
System.out.println("调用yield之前");
Thread.yield();
System.out.println(Thread.currentThread().getName() + "执行");
count++;
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
while (count < 10) {
System.out.println(Thread.currentThread().getName() + "执行");
count++;
}
}
};
t1.start();
t2.start();
}
该程序运行结果如下(多次运行结果不一定一样):

从上述代码及运行结果可推测出:线程调用yiled方法后,让出CPU时间片;所有线程再次抢夺CPU时间片;当再次抢到时间片时再继续执行。观察每次执行的结果,可以发现线程0的在打印 ‘调用yield之前’ 之后,让出了时间片,然后线程0和线程1一起再次抢夺时间片。若线程1抢到时间片之后,执行线程1的run方法,执行完毕之后两个线程再次抢夺时间片。可能是线程1再次抢到再次执行,如第一次运行结果,也可能是线程0抢到,继续执行线程0的run方法(注:是继续执行,并非重新执行),打印 ‘Thread-0执行’ 。