线程基本概念
首先我们要先了解进程的基本概念,因为线程是进程中的一个实体。进程是操作系统中进行资源调度和调度的基本单位,线程则是进程的一个执行路径。
一个进程中包含多个线程,多个线程共享进程的堆和方法区资源,每个线程有独立的程序计数器和栈区域。
线程的三种创建方式
类继承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(); } }
|
那么怎么获得对象监视器锁呢
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(); }
}
|
结果
线程通知
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(); }
}
|
线程的六种状态
NEW
这时候线程已被初始化,不过还没有运行。
RUNNABLE
运行状态,Java线程把操作系统中的就绪和运行两种状态统一称为“运行中”。
TERMINATD
线程结束状态。
BLOCKED
线程堵塞状态,等待获取监视器资源进入同步方法或同步代码块。
WAITING
线程等待状态,等待其他线程,调用Object.wait(),Thread.join()等方法会进入等待状态。
TIMED_WAITING
线程等待一段时间状态,调用Thread.sleep(Long millis)等会进入等待一段时间状态。