线程是什么?
这肯定是初学者在学习线程时遇到的首个问题。
要讲线程,首先要介绍一下进程。进程是就是一个正在运行的程序,是CPU资源分配的单元,进程拥有自己的内存空间,不同的进程不可以共享内存空间。而线程就是进程中的一条执行流程,是操作系统进行计算的最小调度单位,一个进程中可以有多个线程,进程中的所有线程共享这个进程的内存空间。
线程的实现
有了这些简单的基础知识后,那么怎样在Java中创建线程呢?
在Java创建线程有两种方式
1.实现Runnable接口,并实现其中的run方法。
2.继承Thread类,并重写其中的run方法。
既然有两种实现方式,那么着两种方式一定会有区别:
1.我们知道在Java中是不支持多继承的,但允许实现多个接口,所以实现Runnable在这方面具有一定的优势,使得实现类可以继承其他的类,以实现其他的功能。
2.实现Runnable接口的方式,可以使代码在多个线程中共享。但是这就牵涉除了线程安全的问题。
Thread类中的部分方法
1.start()
方法,用于启动一个线程
2.join()
方法,使其他线程等待当前线程终止之后才开始执行或继续执行
3.yield()
静态方法,使得当前线程释放CPU资源,并重新竞争CPU资源。注意该方法会降低Java的可移植性,所以应该使用Thread.sleep(1)
代替Thread.yield()
来进行并发测试,但也不要使用Thread.sleep(0)
,它会立即返回。
线程的状态(生命周期)
1.新建状态,当应用程序新建一个线程时,该线程就处于了新建状态。从编程的角度,该状态叫NEW
状态。
2.可执行状态(就绪状态),当线程实例调用了start()
方法之后,该线程就进入了可执行状态,但是不一定会马上执行。从编程的角度,该状态叫做Runnable
状态。
3.运行状态,当线程调度器为线程分配了CPU时间和资源时,该线程进入了运行状态,此时线程中的代码开始执行。从编程的角度,该状态叫做Running
状态。
4.阻塞状态,当线程调用了自身的sleep()
方法,或对其他线程调用join()
方法,那么该线程就会进入阻塞状态,该状态会停止线程的执行,但不会使线程释放所占有的资源。当sleep()
方法所设置的睡眠时间结束或其他线程执行完执行,该线程进入可执行状态,等待分配CPU时间。从编程的角度,该状态叫做BLOCKED
状态
5.锁池状态,当线程进入可执行状态后,发现将用调用的资源的锁被其他线程持有,,那么该线程进入锁池状态,等待获取资源锁。从编程的角度,实际上该线程仍然被阻塞,所以仍叫做BLOCKED
状态。
6.等待状态,当其他线程调用wait()
方法之后,如果该线程也拥有和其他线程共享的对象,那么该线程就会进入等待状态,进入该状态的线程不会自动唤醒,必须等待共享对象在其他线程调用notify()
或notifyAll()
方法才会被唤醒。从编程的角度,该状态叫做WAITING
状态。如果调用的是wait(long timeout)
或wait(long timeout, int nanos)
方法,那么该状态叫做TIMED_WAITING
状态。
7.终止(死亡)状态,如果当前线程的run()
方法执行结束或main()
方法执行结束后,该线程就会进入终止状态。从编程的角度,该线程叫做TERMINATED
状态。
线程的停止
错误的方法
使用stop()
方法,该方法会使线程戛然而止,发生的过于突然,所以不推荐使用。
正确的方法
设置退出标志位(共享变量),并使用volatile修饰标识或对它的一切访问封入同步块/方法,使得线程安全退出,给予线程有机会进行必要的清理工作。