前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java 多线程学习

java 多线程学习

作者头像
用户1112962
发布2018-07-04 14:13:19
2720
发布2018-07-04 14:13:19
举报
文章被收录于专栏:互联网开发者交流社区

线程的生命周期

new thread(新建):创建一个线程实例, 比如通过 new 操作创建一个 Thread 类的实例, 此时线程未被启动 runnable(可运行):一个线程创建好之后, 需要通知 cpu 这个线程可以开始执行了, 比如 thread 类的 start() 方法执行后, 此时线程在就绪队列中等待 cpu 分配资源 running(运行中):线程获得 cpu 资源后开始运行, 比如运行 run() 方法中的逻辑, 此时除非线程自动放弃 cpu 资源或者有优先级更高的线程进入, 否则将执行到线程结束 dead(死亡):线程正常执行结束, 或者被 kill 调, 此时线程将不会再次被执行 block(阻塞):线程主动让出 cpu 使用权、其它更高优先级的线程进入、该线程的时间片用完,但此时该线程还没有执行完成, 都会使线程进入 block 状态, 进入 block 状态的线程还可以回到就绪队列中等待再次执行。 Thread 类中的方法

start:启动一个线程, 这个方法会是线程进入 Runnable 状态, 等待执行 isAlive:判断线程是否处于活动状态(Runnable 或 running) sleep:强制让线程放弃当前时间片进入休眠状态一定时间, 此时线程会进入 block 状态, 直到休眠的时间结束, 再进入 Runnable 状态。sleep 是静态方法, 只能控制所在线程。sleep(0) 会直接触发下一次 cpu 竞争, 如果没有优先级更高的线程, 则会继续工作 wait(override Object):放弃对象锁, 进入等待池, 只有针对此对象调用 notify() 方法之后, 才会再次进入 Runnable 状态 join:阻塞等待线程结束, 可以接收参数 millis 和 nanos 指定等待的最大时间 interrupt: 中断线程, 这个方法并不能中断正在运行的线程, 运行该方法后, 只有当线程被 join(),sleep() 和 wait() 方法所阻塞时, 才会被 interrupted 方法所中断, 并抛出一个 InterruptedException 异常 static yield:主动放弃 cpu 使用权, 回到 Runnable 状态

block 状态 等待阻塞:运行线程执行了 wait 方法, 该线程会释放占用的所有资源包括对象锁, 进入等待队列中。进入等在队列的线程是不能自动唤醒的, 必须依靠其它线程调用 notify()、notifyAll() 来进行唤醒(该状态下线程会释放对象锁) 同步阻塞:运行的线程在获取对象同步锁时, 同步锁已被其它线程占用, 则该线程会进入锁队列等待获取同步锁, 直到获取到同步锁之后回再次进入 Runnable 状态(该状态下线程还没有获得对象锁) 其它阻塞:运行的线程调用了 sleep()或 join() 方法, 或者发出 IO 请求, 该线程会进入阻塞状态, 知道 sleep 超时、join 所等待的线程结束或是 IO 操作完成, 则会再次进入 Runnable 状态(该状态下线程只会放弃 cpu 而不会释放对象锁) sleep(0) sleep(0) 会重新触发一次 cpu 竞争, 当 Runable 队列中有大于或等于当前线程优先级的线程时, 当前线程会进入 Runnable 队列将 cpu 的使用权让出, 否则会继续运行 sleep()和 wait() sleep 方法会让出 cpu, 但不会释放对象锁, 等到 sleep 超时之后会自动进入 Runnable 队列 wait 方法会让出 cpu, 并释放对象锁, 需要其它线程调用 notify()、notifyAll() 才能重新进入 Runnable 队列 interrupt() interrupt 方法的作用更倾向于告诉线程, 你可以结束了, 而不是直接地中断线程, 知道线程进入阻塞状态时, 才能中断线程。对于陷入死循环、IO 等待等难以进入阻塞状态的线程来说,interrupt 方法是不能有效中断的。 sleep()和 yield() 这两个方法都会让出 cpu 使用权,sleep 会进入 block 状态, 而 yield 会直接进入 Runnable 状态

什么叫做对象锁?

机制   锁机制是用来解决多线程共享资源时产生的冲突问题的。java 为每一个对象关联一个对象锁,通常把锁分为对象锁和类锁,他们的本质都是对象锁,只不过对象锁关联的是类的 Object 对象 (java.lang.Object),而类锁关联的是类的 Class 对象 java.lang.Class。   jvm 对每个锁都有一个计数

若该计数为 0,则锁没有被占用,可以被访问它的线程来持有 一个对象的对象锁已被某个线程持有,新的线程来访问时将被挂起,知道持有它的线程释放该锁并且计数为 0 一个线程已经持有了某个对象的锁,该线程再次访问该对象锁时可以重入,且计数 +1 一个线程释放对象锁时,该锁的计数 -1,当某个锁的计数为 0 时锁被释放,可以被线程竞争

分类 不管怎么分类,java 中通过 synchronized 来实现的锁其本质都是对象锁 java 内部同步机制实现通常有两种方式,synchronized 修饰方法和语句块 synchronized 关键字作用于对象,这个对象可以是类的实例对象,也可以是 Class 对象

类锁

代码语言:javascript
复制
public class SyncObject {
    // 类锁1:通过static方法加同步锁
    public static synchronized void syncMethod1() {
        try {
            System.out.println("testMethod1 start!");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("testMethod1 end!");
    }

    public void syncMethod2() {
        // 类锁2:通过同步语句块传递Class类型参数
        synchronized (SyncObject.class) {
            try {
                System.out.println("testMethod2 start!");
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println("testMethod2 end!");
            }
        }
    }
}

类锁作用于类的 Class 对象,对一个类来说,无论该类有多少个实例,它的静态变量和静态方法都只有一份,保存在 Class 对象中。通过对 Class 对象加锁来实现的同步语句或同步方法,无论该对象的哪个实例来访问,都需要竞争这个类锁

对象锁

代码语言:javascript
复制
public class SyncObject {
    // 对象锁1(方法锁):通过方法加同步锁
    public synchronized void syncMethod1() {
        try {
            System.out.println("testMethod1 start!");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("testMethod1 end!");
    }

    public void syncMethod2() {
        // 对象锁2:通过同步语句块传递Object类型参数
        synchronized (this) {
        // 此处的参数可以是本实例this,也可以是其它实例比如new SyncObject(),传入哪个实例就对哪个实例加锁
            try {
                System.out.println("testMethod2 start!");
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println("testMethod2 end!");
            }
        }
    }
}

  对象锁会对类的实例对象加锁,每一个 new 操作都会获得一个新的实例对象,对于 synchronized 修饰的非 static 方法或传递参数为实例对象的语句块,各个实例对象会拥有自己的锁。

synchronized 的作用范围是对象

synchronized 修饰的代码块需要传入一个对象参数(这个对象可以使 Class 对象),这个对象参数就是它的作用范围

synchronized 修饰的非静态方法的作用范围是 this,即当前对象实例。修饰静态方法时作用范围是这个类,即 Class 对象

synchronized 的粒度针不可再分

因为锁是针对整个对象的,所以当某个对象的锁被持有后,其它线程不能持有该锁

当一个线程调用某个对象 a 的 synchronized 方法(a.syncMethod1)在该方法结束之前,该对象的其它 synchronized 方法 (a.syncMethod2..) 都将不能被调用。同步代码块 synchronized(a)与 a.syncMethod1 有同样的作用周期

当一个线程调用某个类 A 的 static synchronized 方法(A.staticSyncMethod1)在该方法结束前,该类的其它 static synchronized 方法(A.staticSyncMethod2..)都将不能被调用

当一个线程调用某个对象 a 的 synchronized 方法(a.syncMethod1)在该方法结束之前,该类的其它 static synchronized 方法 (A.syncMethod2..) 还可以被调用,反之也成立

synchronized 的只对它修饰的模块加锁

未被 synchronized 修饰的模块是可以被异步访问的, 因为线程访问它的时候并不需要获取对象锁

      • chronized 修饰的代码块需要传入一个对象参数(这个对象可以使 Class 对象),这个对象参数就是它的作用范围
    • synchronized 修饰的非静态方法的作用范围是 this,即当前对象实例。修饰静态方法时作用范围是这个类,即 Class 对象
  1. synchronized 的粒度针不可再分 因为锁是针对整个对象的,所以当某个对象的锁被持有后,其它线程不能持有该锁
    • 当一个线程调用某个对象 a 的 synchronized 方法(a.syncMethod1)在该方法结束之前,该对象的其它 synchronized 方法 (a.syncMethod2..) 都将不能被调用。同步代码块 synchronized(a)与 a.syncMethod1 有同样的作用周期
    • 当一个线程调用某个类 A 的 static synchronized 方法(A.staticSyncMethod1)在该方法结束前,该类的其它 static synchronized 方法(A.staticSyncMethod2..)都将不能被调用
    • 当一个线程调用某个对象 a 的 synchronized 方法(a.syncMethod1)在该方法结束之前,该类的其它 static synchronized 方法 (A.syncMethod2..) 还可以被调用,反之也成立
  2. synchronized 的只对它修饰的模块加锁
    • 未被 synchronized 修饰的模块是可以被异步访问的, 因为线程访问它的时候并不需要获取对象锁
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-03-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档