线程的状态及常用方法

线程的状态

  • 在整个线程的生命周期中,线程的状态有6种:
    • New:新建状态,新创建的线程,此时尚未调用start()方法;相当于创建Thread对象
    • Runnable:运行状态,运行中的线程,已经调用start()方法,线程正在或即将执行run()方法; 相当于开启线程
    • Terminated:终止状态,线程已终止,因此执行run()方法执行完毕。
    • Blocked:阻塞状态,运行中的线程,在等待竞争锁时,被阻塞,暂不执行。相当于线程没抢到锁进入了阻塞状态
    • Wating:等待状态,运行中的线程,因为join()等方法,进入等待;
    • Timed Wating:计时等待状态,运行中的线程,因为执行sleep(等待毫秒值)join(等待毫秒值)等方法,进入及时等待;
    • Terminated:终止状态,现成已终止,因为执行run()方法,执行完毕。

线程终止的原因有:

  • 线程正常终止:run()方法执行到return()语句返回;
  • 线程意外终止:run()方法因为未捕获的异常导致线程终止;
  • 对某个线程的Thread实例调用stop()方法强制终止(不推荐使用,因为过期) 

2.线程插队:join()方法

join()方法的作用

t.join() 方法会让当前线程进入等待池,并等待线程t执行完毕后才会被唤醒。并不影响同一时刻处在运行的其他线程。


public class Test7 {
    public static void main(String[] args) {
        //创建子线程
        Thread t1=new AddThread();
        Thread t2=new  DecThread();

        //启动子线程
        t1.start();
        t2.start();

        //线程插队
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //查看最终结果
        System.out.println(Counter.count);
    }
    static class Counter{
        public static  int count=0;
        public static final  Object lock=new Object();
    }
    //递增线程
    static class AddThread extends Thread{
        public void run(){
            for (int i = 0; i <100000 ; i++) {
                synchronized (Counter.lock){
                    Counter.count+=i;
                }

            }
        }
    }
    //递减线程
    static class DecThread extends  Thread{
        public void run(){
            for (int i=0;i<100000;i++){
                synchronized (Counter.lock){
                    Counter.count-=i;
                }
            }
        }
    }
}

join() 方法的实现原理

  • join()方法的底层是利用wait()方法实现;
  • join()方法是一个synchronized同步方法,当主线程调用线程t.join()方法时,主线程获取线程t的锁,主线程进入wait()方法,等到t线程执行完成后,通过notifyALL()唤醒主线程,主线程释放锁,主线程开始继续执行。
public class Test6 {
    private static void printWithThread(String content){
        System.out.println("["+Thread.currentThread().getName()+"线程]"+content);
    }
    public static void main(String[] args) {
        printWithThread("开始执行main方法");
        //创建子线程
        Thread myThread=new Thread(new Runnable() {
            @Override
            public void run() {
                printWithThread("子线程开始执行run方法");
                printWithThread("即将休息1秒,并让出CPU给别的线程使用");
                try {
                    Thread.sleep(1000);//休眠   timed_wating   当子线程插队时间大于休眠时间,插队成功;当子线程插队时间小于休眠时间则插队失败
                    printWithThread("我已经休息1秒,又重新获得cpu");
                    printWithThread("休息好了,马上退出");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        try {
            //启动子线程
            myThread.start();
            printWithThread("我在main方法里面");//主线程
            myThread.join(1500);//子线程插队     当子线程插队时间大于休眠时间,插队成功;当子线程插队时间小于休眠时间则插队失败
            printWithThread("main线程结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 join()方法和sleep()方法的区别?

  • join()实质是wait()+synchronized实现,所以join()方法结束后会释放锁;
  • sleep()不会释放锁的

线程的中断:interrupt()方法

如果线程需要执行 一个很长时间任务,就可能需要中断线程。中断线程就是其他线程给该线程发一个信号,该线程收到信号后结束执行run()方法,使得自身线程能立刻结束运行。

 interrupt()方法的作用

interrupt()方法的作用是设置该线程的中断状态为true,线程是死亡、还是等待新的任务或者是继续至下一步,就取决于中断状态。线程会不时地检测这个中断状态值,以判断线程是否应该被中断。(中断状态的值是否为true)。

interrupt()方法的原理 

作用:改变线程的中断状态为true,只有支持线程中断的方法才能用线程中断(wait、sleep、join),方法会监视线程中断状态,一旦发现状态为true,就会抛出InterruptedException异常,使方法进入终止状态。

如果线程没有被阻塞或等待,中断方法没有作用

子线程运行时,可以通过isInterrupted()方法随时观察子线程的中断状态

线程让出:yield()方法

yield()方法的作用

  • 线程通过调用yield()告诉JVM的线程调度,当前线程愿意让出CPU给其他线程使用。
  • 至于系统是否采纳,取决于JVM的线程调度模型:分时调度模型和抢占调度模型
    • 分时调度模型:所有的线程轮流获得CPU的使用权,并且平均分配每个线程占用CPU时间片;
    • 抢占式调度模式:优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。

子线程2执行过程中,通过yield()让出CPU,使子线程1执行概率变高。

守护线程

用户线程和守护线程的区别

  • 用户线程:就是创建的普通线程
  • 守护线程:
    1. 清除reference线程    Reference Handler
    2. 调用对象finalize方法的线程   Finalizer
    3. 分发处理给JVM信号的线程    Signal Dispatcher
    4. 添加事件监听器   Attach Listener

设置守护线程

在调用start()方法之前,调用setDaemon(true) 把该线程标记为守护线程

  • 普通用户线程,在没有完成打印内容时,JVM不会结束
  • 守护线程不会影响JVM退出
  • 用户线程的中断结束后,主线程的执行结束,会导致JVM的结束退出

多线程同步

多线程的数据不一致:当多个线程同时操作一段逻辑或代码,可能会出现数据不一致问题

多线程模式下,保证逻辑正确,对变量进行读写时,必须保证一组指令以原子方式执行:即某一个线程执行时,其他线程必须等待。

实现方式:

  1. 加锁
  2. 定义一个具有原子性的变量(AtomicInteger),调用方式实现                                      public static AtomicInteger count=new AtomicInteger(0);  该方式不用加锁

要让两个线程一次只能操作一个线程,如果加锁,必须保证两个线程加的是同一把锁 

创建一个共有的类,当作锁(Object类型的常量)

public static final Object LOCK=new Object();
synchronized (Counter.LOCK){}

当多线程调用两个不同对象同一个类时

  • 第一种锁:Counter1.class等同于this.getClass()
    synchronized (Counter1.class)等同于synchronized (this.getClass())
  • 第二种锁:静态方法上加synchronized等同于this.getClass()                               public static synchronized void toAdd()等同于synchronized (this.getClass())

当多个线程调用同一个对象时

  • 方法上加synchronized等同于synchronized(this)                                      synchronized (this)等同于public synchronized void dec()                           

 

synchronized关键字的用法

修饰普通方法:用到的锁默认为当前方法调用对象

修饰静态方法:其所用的锁,默认为Class对象

修饰代码块:其所用的锁,是指定的某个java对象

当一个线程访问对象的一个synchronized同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块。

父类中synchronized修饰的方法,如果子类没有重写,则该方法仍然是线程安全性;如果子类重写,并且没有使用synchronized修饰,则该方法是线程不安全的

接口方法不能使用synchronized关键字

构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步

离开synchronized代码块后,该线程锁尺有所,会自动释放

synchronized实现原理

底层原理

  使用synchronized保证线程安全,就是保证原子性,使执行过程中不会被其他线程干扰

synchronized代码块是有一对monitorenter/monitorexit指令实现,synchronized是通过对象内部监听器来实现的

每个对象有一个监听器,线程通过执行monitorenter指令尝试获取monitor的所有权,当monitor被占用时就会处于一个阻塞状态;还有一些线程会调用对象的wait(),等wait结束后,抢到的线程获得所有权,其他加入阻塞状态。 

锁升级

偏移锁、轻量级锁、重量级锁

jdk6之前,会直接加重量级锁,导致操作系统需要从用户态转为内核态,资源消耗大

jdk6之后,根据JVM检测到不同的竞争状态时,会自动切换到适合的锁实现

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值