多线程
什么是多线程:
1、如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”
2、多个线程交替占用CPU资源,而非真正的并行执行
多线程好处:
1、充分利用CPU的资源
2、简化编程模型
3、带来良好的用户体验
主线程
Thread类:Java提供了java.lang.Thread类支持多线程编程。
主线程:
1、main()方法即为主线程入口
2、产生其他子线程的线程
3、必须最后完成执行,因为它执行各种关闭动作
线程的创建和启动
在Java中创建线程的两种方式
继承java.lang.Thread类
实现java.lang.Runnable接口
具体实现可以参考Thread类文档注释。
继承类创建线程
定义MyThread类继承Thread类
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
1 | public class MyThread extends Thread{ |
实现接口创建线程
定义MyRunnable类实现Runnable接口
实现run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
1 | public class MyRunnable implements Runnable{ |
run()和start()
调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();
直接调用 run() 方法,无法达到启动多线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。
一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.llegalThreadStateException 异常;
run() 方法没有限制。
线程的状态
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
常见方法
对象的方法
1 public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
2 public void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
3 public final void setName(String name)
改变线程名称,使之与参数 name 相同。
4 public final void setPriority(int priority)
更改线程的优先级。
1 | 每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。 |
5 public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。
6 public final void join(long millisec)
等待该线程终止的时间最长为 millis 毫秒。
7 public void interrupt()
中断线程。
8 public final boolean isAlive()
测试线程是否处于活动状态。
静态方法
1 public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
2 public static void sleep(long millisec)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
3 public static boolean holdsLock(Object x)
当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
4 public static Thread currentThread()
返回对当前正在执行的线程对象的引用。
5 public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流。
线程安全
同步方法
使用synchronized修饰的方法控制对类成员变量的访问
1 | 访问修饰符 synchronized 返回类型 方法名(参数列表){……} |
同步前:
1 | public class Demo05 { |
同步后:
1 | public class Demo05 { |
同步前,运行结果不定,可能是0,也可能是大于0小于2000的任何数,同步后固定结果为0。
同步代码块
使用synchronized关键字修饰的代码块
1 | synchronized(syncObject){ |
syncObject为需同步的对象,通常为this
效果与同步方法相同。
多个并发线程访问同一资源的同步代码块时:
1、同一时刻只能有一个线程进入synchronized(this)同步代码块
2、当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
3、当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码