抱歉,您的瀏覽器無法訪問本站
本頁面需要瀏覽器支持(啟用)JavaScript
了解詳情 >

线程基本概念

首先我们要先了解进程的基本概念,因为线程是进程中的一个实体。进程是操作系统中进行资源调度和调度的基本单位,线程则是进程的一个执行路径。

upload successful

一个进程中包含多个线程,多个线程共享进程的堆和方法区资源,每个线程有独立的程序计数器和栈区域。

线程的三种创建方式

类继承Thread

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestNewThread {

public static class MyThread extends Thread{
@Override
public void run() {
System.out.println("Hello World");
}
}

public static void main(String[] args) {
new MyThread().run();
}
}

类实现Runnable接口

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestNewThread {

public static class RunnableImpl implements Runnable{

@Override
public void run() {
System.out.println("Hello World");
}
}
public static void main(String[] args) {
new Thread(new RunnableImpl()).start();
}
}

类实现Callable接口,使用FutureTask方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TestNewThread {

public static class CallableImpl implements Callable<String>{

@Override
public String call() throws Exception {
return "Hello World";
}
}
public static void main(String[] args) {
/*new MyThread().run();*/
FutureTask<String> futureTask=new FutureTask<>(new CallableImpl());
new Thread(futureTask).start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}

三种方式优劣

1.使用继承方式

优势:
编写简单,传参方便,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
劣势:无法在继承其他父类

2.实现Runnable接口方式和Callable接口

优势:
程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况。
callable还可以拿到线程的返回值
劣势:编写稍微稍微复杂点?

线程的休眠和等待

wait()方法

在java所有类的直接或间接父类Object类中有个wait(),当调用时会让当前线程堵塞挂起。

注意:调用对象的wait()时,要确保获得对象的监视器锁。

反例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestThreadWait {
private static Object lock=new Object();
public static void main(String[] args) {
new Thread(()->{
System.out.println("🔒前");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("🔒后");
}).start();
}
}

upload successful

那么怎么获得对象监视器锁呢
1.执行synchronized同步代码块时,并且使用该对象做为锁

1
2
3
       synchronized(lock) {
//doSomething
}

2.调用对象的synchronizeed方法

1
2
3
public synchronized void method(){
//doSomething
}

sleep()方法

sleep()是Thread线程类的一个静态方法,执行时当前线程会暂时让出对cpu的执行权,但是注意休眠的时候不会退出锁(线程所拥有的监视器资源)。当休眠时间到了就会继续正常运行,当还在休眠时间时,调用该休眠线程的interrupt()方法时,该休眠线程就会在调用sleep()方法的地方抛出InterruptedException异常返回。

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class TestThreadWait {
private static Object lock=new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized(lock) {
System.out.println("线程一休眠前");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程一休眠后");
}
}).start();

new Thread(()->{
synchronized(lock) {
System.out.println("线程二休眠前");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程二休眠后");
}
}).start();
}


}
结果

upload successful

线程通知

notify()方法和notifyAll()方法

执行notify/notifyAll方法会唤醒一个/多个正处于休眠状态的线程。然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。
类似wait方法,当前线程获取到对象的锁(监视器资源)才可以执行notify/notifyAll方法,否则会抛出异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class TestThreadWait {
private static Object lock=new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized(lock) {
System.out.println("线程一休眠前");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程一休眠后");
}
}).start();

new Thread(()->{
synchronized(lock) {
System.out.println("尝试换线休眠线程");
lock.notify();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("唤醒完毕");
}).start();
}


}

线程的六种状态

upload successful

NEW

这时候线程已被初始化,不过还没有运行。

RUNNABLE

运行状态,Java线程把操作系统中的就绪和运行两种状态统一称为“运行中”。

TERMINATD

线程结束状态。

BLOCKED

线程堵塞状态,等待获取监视器资源进入同步方法或同步代码块。

WAITING

线程等待状态,等待其他线程,调用Object.wait(),Thread.join()等方法会进入等待状态。

TIMED_WAITING

线程等待一段时间状态,调用Thread.sleep(Long millis)等会进入等待一段时间状态。