点击上方 Java 知音, 选择置顶公众号
技术文章第一时间送达!
知音专栏:
Javaweb 练手项目源码下载
常用的设计模式完整总结篇
近几年的 java 面试题总结汇总
Ø 线程的概述(Introduction)
线程是一个程序的多个执行路径, 执行调度的单位, 依托于进程存在 线程不仅可以共享进程的内存, 而且还拥有一个属于自己的内存空间, 这段内存空间也叫做线程栈, 是在建立线程时由系统分配的, 主要用来保存线程内部所使用的数据, 如线程执行函数中所定义的变量
注意: Java 中的多线程是一种抢占机制而不是分时机制抢占机制指的是有多个线程处于可运行状态, 但是只允许一个线程在运行, 他们通过竞争的方式抢占 CPU
Ø 线程的定义(Defining)
定义一个线程 (Defining a Thread) 有两种方法
1) 继承 java.lang.Thread 类
- /**
- * 使用继承 java.lang.Thread 类的方式创建一个线程
- *
- * @author DreamSea 2011-12-29 20:17:06
- */
- public class ThreadTest extends Thread {
- /**
- * 重写 (Override)run() 方法 JVM 会自动调用该方法
- */
- public void run() { System.out.println("I'm running!");
- }
- }
注意: 重写 (override)run() 方法在该线程的 start()方法被调用后, JVM 会自动调用 run 方法来执行任务; 但是重载 (overload)run() 方法, 该方法和普通的成员方法一样, 并不会因调用该线程的 start()方法而被 JVM 自动运行 例如:
- public class ThreadTest extends Thread {
- /**
- * 重写 (Override)run() 方法 JVM 会自动调用该方法
- */
- @Override
- public void run() {
- System.out.println("I'm running!");
- }
- /**
- * 重载 (Overload)run() 方法 和普通的方法一样,
- * 并不会在该线程的 start()方法被调用后被 JVM 自动运行
- */
- public void run(int times) {
- System.out.println("I'm running!(Overload)");
- }
- }
不建议使用此方法定义线程, 因为采用继承 Thread 的方式定义线程后, 你不能在继承其他的类了, 导致程序的可扩展性大大降低
2) 实现 java.lang.Runnable 接口
- /**
- * 通过实现 Runnable 接口创建一个线程
- * @author DreamSea
- */
- public class ThreadTest implements Runnable {
- public void run() {
- System.out.println("I'm running!");
- }
- }
Ø 线程的启动(Starting)
任何一个线程的执行的前提都是必须有 Thread class 的实例存在, 并且通过调用 start 方法启动线程
1)如果线程是继承 Thread 类, 则创建方式如下:
- ThreadTest1 tt = new ThreadTest1();
- tt.start();
2)如果是实现 Runnable 接口, 则创建方式如下:
- ThreadTest2 tt = new ThreadTest2();
- Thread t = new Thread(tt);
- t.start();
Ø 线程的状态(State)
新生状态 (New): 当一个线程的实例被创建即使用 new 关键字和 Thread 类或其子类创建一个线程对象后, 此时该线程处于新生(new) 状态, 处于新生状态的线程有自己的内存空间, 但该线程并没有运行, 此时线程还不是活着的(not alive);
就绪状态 (Runnable): 通过调用线程实例的 start() 方法来启动线程使线程进入就绪状态(runnable); 处于就绪状态的线程已经具备了运行条件, 但还没有被分配到 CPU 即不一定会被立即执行, 此时处于线程就绪队列, 等待系统为其分配 CPCU, 等待状态并不是执行状态; 此时线程是活着的(alive);
运行状态 (Running): 一旦获取 CPU(被 JVM 选中), 线程就进入运行(running) 状态, 线程的 run()方法才开始被执行; 在运行状态的线程执行自己的 run()方法中的操作, 直到调用其他的方法而终止或者等待某种资源而阻塞或者完成任务而死亡; 如果在给定的时间片内没有执行结束, 就会被系统给换下来回到线程的等待状态; 此时线程是活着的(alive);
阻塞状态 (Blocked): 通过调用 join()sleep()wait() 或者资源被暂用使线程处于阻塞 (blocked) 状态; 处于 Blocking 状态的线程仍然是活着的(alive)
死亡状态 (Dead): 当一个线程的 run() 方法运行完毕或被中断或被异常退出, 该线程到达死亡 (dead) 状态此时可能仍然存在一个该 Thread 的实例对象, 当该 Thready 已经不可能在被作为一个可被独立执行的线程对待了, 线程的独立的 call stack 已经被 dissolved 一旦某一线程进入 Dead 状态, 他就再也不能进入一个独立线程的生命周期了对于一个处于 Dead 状态的线程调用 start()方法, 会出现一个运行期 (runtime exception) 的异常; 处于 Dead 状态的线程不是活着的(not alive)
线程状态图
Ø 线程的方法 (Method) 属性(Property)
1)优先级(priority)
每个类都有自己的优先级, 一般 property 用 1-10 的整数表示, 默认优先级是 5, 优先级最高是 10; 优先级高的线程并不一定比优先级低的线程执行的机会高, 只是执行的机率高; 默认一个线程的优先级和创建他的线程优先级相同;
2)Thread.sleep()/sleep(long millis)
当前线程睡眠 / millis 的时间 (millis 指定睡眠时间是其最小的不执行时间, 因为 sleep(millis) 休眠到达后, 无法保证会被 JVM 立即调度);sleep()是一个静态方法 (static method) , 所以他不会停止其他的线程也处于休眠状态; 线程 sleep() 时不会失去拥有的对象锁 作用: 保持对象锁, 让出 CPU, 调用目的是不让当前线程独自霸占该进程所获取的 CPU 资源, 以留一定的时间给其他线程执行的机会;
3)Thread.yield()
让出 CPU 的使用权, 给其他线程执行机会让同等优先权的线程运行 (但并不保证当前线程会被 JVM 再次调度使该线程重新进入 Running 状态), 如果没有同等优先权的线程, 那么 yield() 方法将不会起作用
4)thread.join()
使用该方法的线程会在此之间执行完毕后再往下继续执行
5)object.wait()
当一个线程执行到 wait()方法时, 他就进入到一个和该对象相关的等待池 (Waiting Pool) 中, 同时失去了对象的机锁暂时的, wait 后还要返还对象锁当前线程必须拥有当前对象的锁, 如果当前线程不是此锁的拥有者, 会抛出 IllegalMonitorStateException 异常, 所以 wait()必须在 synchronized block 中调用
6)object.notify()/notifyAll()
唤醒在当前对象等待池中等待的第一个线程 / 所有线程 notify()/notifyAll()也必须拥有相同对象锁, 否则也会抛出 IllegalMonitorStateException 异常
7)Synchronizing Block
Synchronized Block / 方法控制对类成员变量的访问; Java 中的每一个对象都有唯一的一个内置的锁, 每个 Synchronized Block / 方法只有持有调用该方法被锁定对象的锁才可以访问, 否则所属线程阻塞; 机锁具有独占性一旦被一个 Thread 持有, 其他的 Thread 就不能再拥有(不能访问其他同步方法), 方法一旦执行, 就独占该锁, 直到从该方法返回时才将锁释放, 此后被阻塞的线程方能获得该锁, 重新进入可执行状态
来源: https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247484873&idx=1&sn=500c2c8ff4e9fb7577b9e00b08183163&chksm=ebd63ae5dca1b3f3621684b545e3842f16dd26e6aa4fe1a563a00235300358fb21abcfe1947b#rd