【Thread】Java Thread 线程

线程

thread 是 程序中的 执行线程。Java 虚拟机 允许应用程序同时有多个 执行线程 在运行。

每个线程 都有优先级。优先级较高的线程 优先于 优先级较低的线程 执行。当某个运行中的线程 创建了一个新的线程对象,那么这个新线程的优先级等于创建线程的优先级,并且当且仅当创建线程是守护守护线程时,新线程才是守护线程。

当 Java 虚拟机启动时,通常会有一个 非守护线程(通常用来调用某些指定类的 main 方法)。Java 虚拟机继续执行线程,直到以下任一个发生:

  • RunTime 类的 exit 方法被调用,并且 安全管理器 允许 退出操作发生。
  • 所有非守护线程都已经消亡。

有两种方式创建 一个 新的执行线程。

  1. 声明一个 继承Thread 类 的子类,并重写 Thread 类的 run 方法。然后就可以实例化子类 并调用 start 方法。
  2. 声明一个 实现 Runnable 接口的类,并实现 run 方法。然后实例化该类,并作为参数传递给 Thread,最后调用 start 方法。

线程状态

  • 新建状态(new)

还没有开始的线程,即还没有调用 start 方法执行的线程。

  • 可运行状态(runnable)

正在 Java 虚拟机中执行,但是它可能正在等待来自操作系统的其他资源,比如 处理器。

  • 阻塞状态(blocked)

正在等待监视器锁(monitor lock)的线程。调用 Object 的 wait 方法需要在 同步方法/同步代码块中调用,并且必须使用监视器锁对象调用 wait 方法,否则会抛出 IllegalMonitorStateException 异常。之后线程进入 等待状态,当其他线程中调用了 锁对象的 notify 方法 或者任意一个对象的 notifyAll 方法,则 线程苏醒,此时,若锁对象的锁正在被其他线程使用,则 线程处于 阻塞状态,否则进入 可运行状态,等待 操作系统资源。

  • 等待状态(waiting):

线程因为调用以下方法中的一个而进入到等待状态

  1. Object.wait 方法,没有超时时间参数;
  2. Thread.join 方法,没有超时时间参数;
  3. LockSupport.park 方法;

等待中的线程正在等待另一个线程执行特殊的操作来唤醒。例如 wait 方法 需要调用 对应Object 的 notify 或者 notifyAll 方法。

  • 指定时间的等待状态(timed_waiting)

拥有指定等待时间的等待线程。

因为调用以下方法中的一个而进入该状态

  1. Object 的 wait(long) 方法,带有时间参数。
  2. Thread 的 join(long) 方法,带有时间参数;
  3. LockSupport 的 parkNanos 方法;
  4. LockSupport 的 parkUntil 方法;
  • 消亡状态(terminated)

已终止的线程:线程已经完成执行。

线程 sleep 和 wait 的区别

  • wait 方法必须在同步上下文中调用,比如在同步代码块 或者 同步方法中调用,否则会抛出 IllegalMonitorStateException  异常。sleep 不需要。
  • wait 是 Object 类中的方法,是非静态方法。sleep 是在当前线程上操作并且方法是定义在 Thread 类中,是静态方法。
  • wait 方法会释放其调用的对象锁,如果它持有其他锁,同时释放其他锁。 sleep 方法 不会释放任何锁。

同步 synchronization

线程共享数据访问 会产生2个问题

  • 线程冲突(thread interference )
  • 内存一致性错误(memory consistency errors)

避免这两个问题的方法是 同步(synchronization)。

同步是围绕一个称为内部锁(intrinsic lock) 或者 监视器锁(monitor lock)的 内部实体类来构建的。(API 规范通常把这个实体简单地称为 "监视器")。

每个对象都有一个与其相关的内部锁。按照惯例,需要对对象的字段进行独占和一致性访问的线程必须在访问对象之前获取对象的内部锁,在访问完成之后释放内部锁。有且只有一个线程可以获取一个对象的内部锁,当其他线程试图获取锁时,线程会进入 阻塞状态(blocked)。

同步方法

同步方法自动获取该方法对象的内部锁,并在方法返回时释放。

以下两个非静态方法的等价:

        public synchronized void sync() {
            
        }

        public  void syncBlock() {
            synchronized(this) {
                
            }
        }

对于静态方法,因为静态方法是和类相关的,和对象不相关,所以,线程自动获取 和类相关的类对象的内部锁。

以下两个静态方法的等价:

        public static synchronized void syncStatic() {

        }

        public static synchronized void syncStaticBlock() {
            synchronized (类名.class) {

            }
        }
  • 调用同一个对象中非静态同步方法的线程是互斥的。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。
  • 调用同一个类中的静态同步方法的线程将是互斥的,它们都是锁定在相同的Class对象上。
  •  静态同步方法和非静态同步方法将永远不是互斥的,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。
  •  对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将是互斥的,在不同对象上锁定的线程将永远不会互斥

同步代码块

同步代码块必须手动指定 提供内部锁的对象。

死锁 DeadLock

死锁 是 两个 或者 多个线程永远阻塞,互相等待的现象。

死锁案例:

public class DeadLock {

    static class Friend {
        private final String name;

        public Friend(String name) {
            this.name = name;
        }

        public String getName() {
            return this.name;
        }

        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s"
                            + "  has bowed to me!%n",
                    this.name, bower.getName());
            bower.bowBack(this);
        }

        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s"
                            + " has bowed back to me!%n",
                    this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse = new Friend("Alphonse");
        final Friend gaston = new Friend("Gaston");
        Thread t1 = new Thread(() -> alphonse.bow(gaston));
        t1.start();
        Thread t2 = new Thread(() -> gaston.bow(alphonse));
        t2.start();
    }
}

理论分析:

线程 t1 实例化,处于 new 状态,调用 start 方法,进入 Runnable 状态。如果此时系统资源满足即运行 run 方法:alphonse.bow(gaston)。调用 Friend 静态内部类的 bow 同步方法,此时的 需要获取 alphonse 对象锁(监视器锁)才能进入到同步方法 。bow 方法内部又调用了 bower.bowBack 方法:此时的 bower 为 gaston,this 为 alphonse ,所以执行 bowBack 方法需要再获取 gaston 对象锁才能进入 bowBack 同步方法

线程 t2 实例化,并调用 start 方法,执行内部 run 方法 为 :gaston.bow(alphonse);调用Friend 静态内部类的 bow 同步方法,此时需要获取 gaston 监视器锁才能进入同步方法。bow 方法内部调用 bower.bowBack 方法:此时的 bower 为 alphonse,this 为 gaston,所以需要再获取 alphonse 对象锁才能进入 bowBack 同步方法

为什么产生死锁:

线程 t1 能够顺利 获取到  alphonse 对象锁 执行 bow 方法,此时 线程 t2 也能够顺利获取到 gaston 对象锁 执行 bow 方法。接着 t1 想要在不释放 alphonse 对象锁的情况下 再次 获取到 gaston  对象锁,此时  gaston  对象锁 正在被 线程 t2 使用,所以此时 t1 进入阻塞状态(blocked),等待 t2 释放  gaston  对象锁,与此同时,t2 也想要在不释放gaston 对象锁的情况 再次获取到 alphonse 对象锁,而此时的alphonse 对象锁 正在被 t1 使用,那么 t2 也进入了阻塞状态(blocked),等待 t1 释放alphonse 对象锁。就这样两个线程都被阻塞,互相等待对方释放锁才能执行,这就产生了所谓的死锁。

 

——更多内容,参看 Oracle 官方文档

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值