一、线程的状态
- New新建
- Runnable可运行
- Running运行
- Blocked阻塞
- Dead死亡
New新建,就是创建一个线程。
Runnable可运行,线程启动start,等待cpu调度。
Running运行,线程正在执行,cpu正在调度。
Blocked阻塞。由于某种原因,线程暂停,释放cpu的使用权。直到结束,重新返回就绪状态,等待cpu调用。
Dead死亡,线程成功或不成功的退出,完成了整个生命周期。
二、线程的创建和启动方式
1、Thread继承
继承Thread的方式,重写run方法。
class MyThread extends Thread {
private int i = 0;
@Override
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public class ThreadTest {
public static void main(String[] args) {
Thread myThread = new MyThread(); // 创建一个新的线程 myThread 此线程进入新建状
myThread.start(); // 调用start()方法使得线程进入就绪状态
}
}
2、实现Runnable接口
实现Runnable的接口,重写run方法。
class MyRunnable implements Runnable {
private int i = 0;
@Override
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public class ThreadTest {
public static void main(String[] args) {
Runnable myRunnable = new MyRunnable(); // 创建一个Runnable实现类的对象
Thread thread = new Thread(myRunnable); // 将myRunnable作为Thread target创建新的线程
thread.start(); // 调用start()方法使得线程进入就绪状态
}
}
有一个问题如果有这么一个类实现Runnable接口,有个类继承Thread,那么问题来了,线程到底运行那个程序。
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("this is MyRunnable");
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("this is MyThread");
}
}
public class ThreadTest {
public static void main(String[] args) {
Runnable myRunnable = new MyRunnable();
Thread myThread = new MyThread(myRunnable);
myThread.start();
}
}
根据实践,得出真正运行的是MyThread中的run。因为Thread中继承的Runnable,在Thread中run中
@Override
public void run() {
if (target != null) {
target.run();
}
}
其中这个target就是Runnable,又因为子类重写了这个run,所以运行的是子类的run。
3、使用Callable和Future接口创建
这个方式可以使用线程的返回值进行其他操作。
class MyCallable implements Callable<Integer> {
private int i = 0;
// 与run()方法不同的是,call()方法具有返回值
@Override
public Integer call() {
int sum = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
sum += i;
}
return sum;
}
}
public class ThreadTest {
public static void main(String[] args) throws Exception{
Callable<Integer> myCallable = new MyCallable(); // 创建MyCallable对象
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask来包装MyCallable对象
Thread thread = new Thread(ft); //FutureTask对象作为Thread对象的target创建新的线程
thread.start(); //线程进入到就绪状态
int sum = ft.get(); //取得新创建的新线程中的call()方法返回的结果
System.out.println("sum = " + sum);
}
}
三、线程的阻塞
好多情况会造成线程的阻塞。大概分成3类:等待阻塞、同步阻塞、人为阻塞。
1、等待阻塞
等待阻塞是等待wait(),wait/notify/notifyAll是Object对象的内容,会阻塞线程,释放锁。所以这些方法都要写在临界区内。
public class WaitTest {
public void waitMethod(Object obj){
try {
synchronized (obj) {
obj.wait();
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
public void notifyMethod(Object obj){
try {
synchronized (obj) {
obj.notify();
Thread.sleep(100);
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class ThreadA extends Thread {
private Object obj;
public ThreadA(Object obj) {
this.obj = obj;
}
@Override
public void run() {
WaitTest waitTest = new WaitTest();
waitTest.waitMethod(obj);
}
}
public class ThreadB extends Thread {
private Object obj;
public ThreadB(Object obj) {
this.obj = obj;
}
@Override
public void run(){
WaitTest waitTest = new WaitTest();
waitTest.notifyMethod(obj);
}
}
public class ThreadTest {
public static void main(String[] args) {
Object lock = new Object();
Thread A = new ThreadA(lock);
Thread B = new ThreadB(lock);
A.start();
B.start();
}
}
2、同步阻塞
同步阻塞是等待同步锁释放,从而回到就绪状态。有synchronized、lock
public class WaitTest {
public void notifyMethod() {
synchronized (this) {
Integer sum = 0;
for (int i = 1; i < 100; i++) {
sum++;
}
}
}
}
public class MyThread extends Thread {
private WaitTest waitTest;
public MyThread(WaitTest waitTest) {
this.waitTest = waitTest;
}
@Override
public void run() {
waitTest.notifyMethod();
}
}
public class ThreadTest {
public static void main(String[] args) {
Object lock = new Object();
WaitTest waitTest = new WaitTest();
Thread A = new MyThread(waitTest);
Thread B = new MyThread(waitTest);
A.start();
B.start();
}
}
3、人为阻塞
人为阻塞是通过join等方法将线程堵塞,等待返回。有sleep、join、等待I/O等。
public class MyThread extends Thread {
@Override
public void run() {
Integer sum = 0;
for (int i = 1; i < 100; i++) {
sum++;
}
}
}
public class ThreadTest {
public static void main(String[] args) {
Thread A = new MyThread();
A.start();
try {
A.join();
Integer sum = 0;
for (int i = 1; i < 100; i++) {
sum++;
}
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
四、同步的方式
线程同步,是为了避免不同线程访问共享资源时产生不确定结果。线程通信是指,各个线程协同工作,需要相互通知。线程同步常用同步块和锁,线程通信常用等待和通知。
1、synchronize
使用synchronize关键字来标记同步块或者时同步方法,当且只有一个线程可以执行。
2、lock
lock类的灵活性比synchronize好,也是用于同步代码块,做到线程安全访问。
3、wait和notify或notifyAll
大多数情况下用于线程间通信,相互通知。
4、join
这种大多数用于需要运行完某个线程,这个做线程安全和线程通信有点鸡肋。
5、yield
这种更极端,直接线程让步,但是让步不一定能保证线程安全和线程通信安全。