美文网首页程序员半栈工程师
java中Thread的深入了解(一)

java中Thread的深入了解(一)

作者: Charon_Pluto | 来源:发表于2017-09-05 10:07 被阅读85次

    线程

            线程是进程中的一个单一顺序的执行单元,也被称为轻量进程(lightweight process)。线程有自己的必须资源,同时与其他线程进程共享全部资源。同一个进程中,线程是并发执行的。由于线程的之间的相互制约,线程有就绪、阻塞、运行、结束等状态。

    一 新建线程

    我们先创建两个线程类:

    新建一个MyThread类 继承 Thread来覆盖 run方法:

    MyThread类

    新建MyRunnable类 实现Runnable 接口:

    MyRunable类

    创建以及执行两个线程类

    主线程Main 线程结果

    二 验证线程并发

    在上述创建的MyThread类和MyRunnable类各自的run方法输出上 加个for循环for(int i=0;i<10000;i++);

    循环线程 输出结果extends

    如果是顺序执行,应该会是 extendsThread全部输出完 ,才输出implement thread。但是这并不是而是出现交叉结果 证明线程是并发执行。

    交叉原理就是每个代码 在cpu时间片内执行一段时间,到时间就换一个代码 在cpu时间片内执行,但是如果时间片内打印完了,就看不见交叉现象。

    单核cpu

    三 线程声明周期(或者线程状态)

    java中线程基本上有以下图片中的几种状态:

    线程状态

    首先我们要做一个输出线程状态的类:

    线程状态

    public static void printThreadState(Thread thread){

    Thread.State state=thread.getState();

    switch(state){

    //1.新建状态

    caseNEW:

    info(thread.getName()+" state is NEW");break;

    //2.执行状态

    case RUNNABLE:

    info(thread.getName()+" state is RUNNABLE");break;

    //3.等待超时状态

    case TIMED_WAITING:

    console.info(thread.getName()+" state is TIMED_WAITING");break;

    //4.等待状态

    case WAITING:

    info(thread.getName()+" state is WAITING");break;

    //5.阻塞状态

    case BLOCKED:

    info(thread.getName()+" state is BLOCKED");break;

    //6.结束状态

    caseTERMINATED:

    info(thread.getName()+" state is TERMINATED");break;

    default:

    info

    (thread.getName()+" state is UNKOWN");

    }

    }

    //打印

    public static  void info(Object o){

    System.out.println(String.valueOf(o));

    }

    1.新建状态:

    new之后 start之前都属于NEW状态,一旦状态变成其他,则不会再变成NEW状态。

    public static  void main(String args[]) throws InterruptedException{

    Thread t=new Thread(new Runnable() {

    @Override

    public void run() {

    }

    });

    printThreadState(t);//查看new之后的thread状态

    }

    2.执行状态

    在run()方法里的时候,线程处于执行状态。一旦run()方法执行完毕,则状态就不是执行状态了

    执行状态 线程状态输出结果

    3.等待超时状态

    当调用wait(long timeout)方法的时候,线程处于等待状态,并且超时后并不马上返回,而是等待获取锁后返回。

    查看wait(long timeout) 输出结果

    验证超时后并不马上返回,而是等待获取锁后返回。

    添加一条输出结果 如果是wait(long timeout)方法时候,提前先把锁获取过来等待20s 会发现输出结果线程还是阻塞状态

    所以我们会发现一个问题,wait的超时只是一个预想, 多少时间后我再去获取锁,如果拿到就返回 拿不到就处于等待状态。这里就会发现这里等待是最少需要long timeout时间。

    wait(等多少时间后 再去竞争锁),wait的时间内 我不去竞争 跟别人抢锁,所以这个时间 不保证锁一定能抢回来。

    注意:Thread.sleep 也会进入TIME_WAITING状态。

    5.等待状态

    wait()睡眠不参与抢锁,等待notify来进行唤醒。

    放弃抢锁,处于waiting状态 waiting状态时候唤醒 输出结果

    wait 也可以通过interrupt唤醒,interrupt是中断线程的方法。

    5.阻塞状态

    如果获取不到锁,就一直在阻塞状态,直到获取锁为止。

    把循环锁死,当状态位锁死的时候释放锁 输出结果

    6.结束状态

    在run()方法执行结束以后,线程处于结束状态

    结束状态

    但是当这个线程执行结束了之后,是否代表这个线程被销毁了呢。然而只是线程执行单元销毁了,但是线程对象还在。虽然这个线程对象还存在,但是它已经不能进行再次的start()方法进行执行了。

    四 研究多线程锁问题

    经典并发问题:当运行一万个线程想List插入的时候,预期结果是有10000个,但是输出确不是10000个,而是少于1000个。原因是 线程并发执行获取list的长度是过期的。

    比如我拿到list ,你也拿到list ,咱俩同时看list里有1条数据 我把数据插入第二个位置 ,你也插入第二个位置 ,就会导致list最终结果只有2条数据,实际上插入了3条数据。

    public static void ThreadMuliteTest() throws InterruptedException {

    final List list=new ArrayList();

    for(int i=0;i<10000;i++){

    new Thread(new Runnable() {

    @Override

    public void run() {

    list.add(Thread.currentThread().getName());

    }

    }).start();

    }

    Thread.sleep(1000);

    System.out.println(list.size());

    }

    输出结果

    这就是线程并发问题,并发问题有个解决办法 就是对线程进行枷锁让他排队,我拿到了 你就不能拿。

    synchronized 代码段是java给线程的一个控制权,可以锁住一个资源,防止被其他人用。加锁之后线程就进入了 排队状态 ,只有一个线程能拿到list的权限 其他等待。

    还有一种操作 就是我得到资源后 ,可以释放一段时间给别人用 ,超时了 我在拿回来自己用。

    public static void ThreadGetWait() throws InterruptedException {

    Object lock=new Object();

    new Thread(new Runnable() {

    @Override

    public void run() {

    synchronized (lock){

    console.info("线程一:拿到锁");

    try {

    lock.wait(100);//释放100ms

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    console.info("线程一:锁又被我拿回来了");

    try {

    Thread.sleep(1000);//睡1s 锁不给别人

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    }).start();

    Thread.sleep(10);//休眠10ms防止第二个线程拿到锁

    new Thread(new Runnable() {

    @Override

    public void run() {

    synchronized (lock){

    console.info("线程二:拿到锁");

    try {

    Thread.sleep(2000);//拿到锁后我要休眠2s

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    console.info("线程二:锁又回来了?");

    }

    }

    }).start();

    }

    输出结果

    因为线程1先运行 肯定拿到锁,线程一暂时释放。所以线程二紧接着拿到了锁,线程二休眠了2s 期间并没有释放锁,所以线程1一直处于阻塞状态,线程二执行完成后 释放锁 线程一 打印 最后一句。

    相关文章

      网友评论

        本文标题:java中Thread的深入了解(一)

        本文链接:https://www.haomeiwen.com/subject/qqdkjxtx.html