多线程
一个线程的生命周期
多个线程之间交替执行,无规律顺序,不确定次数
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
-
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
-
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
-
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
-
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
-
- 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
- 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
- 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
-
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
创建一个线程
Java 提供了三种创建线程的方法:
-
通过实现 Runnable 接口;
class RunnableDemo implements Runnable {}
-
通过继承 Thread 类本身;继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。
该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个示例。
class ThreadDemo extends Thread {}
-
通过 Callable 和 Future 创建线程。
-
线程池
Thread 方法
下表列出了Thread类的一些重要方法:
序号 | 方法描述 |
---|---|
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) 更改线程的优先级。 |
5 | public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。 |
6 | public final void join(long millisec) 阻塞外面的线程,执行这个线程,等待该线程终止的时间最长为 millis 毫秒。 |
7 | public void interrupt() 中断线程。 |
8 | public final boolean isAlive() 测试线程是否处于活动状态。 |
主线程向子线程传值
构造方法
线程同步
- 多个线程调用同一个方法,公用同一份代码
- 各线程所用方法的局部变量各自维护 线程计数器 记录上一次执行被阻塞的地方
- 程序分好几次执行
当两个线程在执行同一份代码的时候,并不是一个线程执行完了,再让另一个线程执行。很有可能一个线程还没执行完,就被第二个线程阻塞了。一个方法也不是一次执行完也有可能是两次或者三次才执行完。它有个线程计数器,记录这个线程上一次执行到了哪行代码,等这个线程能继续执行的时候,会继续执行剩下的代码。
线程安全问题
线程安全问题: 线程同步+单例+有成员变量
一段代码在多线程下运行的逻辑和数据是正确,就是线程安全的。
线程安全问题就是在线程同步的基础上,多个线程操作到了同一个成员变量(注意是成员变量不是局部变量)。线程安全问题一般指的是单例的情况: 单例情况下 (单例就是一个对象),这个对象被多个线程操作,并且这个对象有成员变量,就会出现线程安全问题。因为单例,多个线程在操作同一个对象,它们就会访问到同一份成员变量。
多例情况下: 多例就是new了多个对象,线程使用的不是同一个对象。即使有成员变量,这个成员变量也是每个线程自己维护一份,它们不会访问到同一份成员变量,所以多例情况下,即使有成员变量也是线程安全的。
解决线程安全问题
1.调用者为this时可以使用synchronized锁住不能同步的代码 ()括号内为锁对象,必须唯一 全局唯一 类属性 , 类的class对象 (字节码对象)
总结:
synchronized的锁对象,我们使用了三种情况
o对象:默认会这样使用,就是找了一个唯一的锁对象
this: 因为这里的Stu是单例的,所以可以把它作为锁对象。IStu.class: 因为这里的Stu是多例的,所以只能锁住stu的Class对象,因为它在内存里也是唯一的。字节码对象是唯一。