Java-线程的运行状态

参考:
学习java线程状态和看懂thread dump文件中的线程信息
Java线程中wait状态和block状态的区别?

1,线程状态为“waiting for monitor entry”:
意味着它 在等待进入一个临界区 ,所以它在”Entry Set“队列中等待。
此时线程状态一般都是 Blocked:
java.lang.Thread.State: BLOCKED (on object monitor)

2,线程状态为“waiting on condition”:
说明它 在等待另一个条件的发生,来把自己唤醒,或者干脆它是调用了 sleep(N)。
此时线程状态大致为以下几种:
java.lang.Thread.State: WAITING (parking):一直等那个条件发生;
java.lang.Thread.State: TIMED_WAITING (parking或sleeping):定时的,那个条件不到来,也将定时唤醒自己。

参考:
多线程等待唤醒机制:从wait()和sleep()的差别说起
深入理解Java多线程中的wait(),notify()和sleep()

这里写图片描述

wait与sleep区别:

wait():释放资源,释放锁,(放弃执行资格)这样另一个线程得以进入并执行,
Object的方法,用在同步当中,是同步锁的方法,以锁控制线程
sleep():释放资源,不释放锁,Thread的静态方法,
sleep方法会自动唤醒,如果时间不到,想要唤醒,可以使用interrupt方法强行打断。

同步:两个或以上线程,使用同一把锁

sleep可以在任何地方使用。而wait,notify,notifyAll只能在同步控制方法或者同步控制块中使用。
sleep必须捕获异常,而wait,notify,notifyAll的不需要捕获异常。

waitting与blocked区别:

假设t1,t2先后两个线程,都执行如下代码:

synchronized(Obj) {
    Obj.wait(); 
}

t1先进,最后在Obj.wait()下卡住,这时java管t1的状态waitting
状态t2后进,直接在第一行就卡住了,这时java叫t2为blocked状态。

    public enum State {
        NEW,

        RUNNABLE,

        /**
         * 线程阻塞,当前线程在等待一个monitor lock,比如等待执行synchronized代码块或者使用synchronized标记的方法。
         * 在synchronized块中循环调用Object类型的wait方法.
         */
        BLOCKED,

        /**
         * 线程等待.
         * 线程处于WAITING状态的场景:
         * 调用Object对象的wait方法,但没有指定超时值。
         * 调用Thread对象的join方法,但没有指定超时值。
         * 调用LockSupport对象的park方法。
         */
        WAITING,

        /**
         * 线程处于TIMED_WAITING状态的场景。
         * 调用Thread.sleep方法。
         * 调用Object对象的wait方法,指定超时值。
         * 调用Thread对象的join方法,指定超时值。
         * 调用LockSupport对象的parkNanos方法。
         * 调用LockSupport对象的parkUntil方法。 
         */
        TIMED_WAITING,

        TERMINATED;
    }

线程基本状态图:


图中是线程运行的基本状态:线程调用start()方法开始后,就进入到可运行状态,随着CPU的资源调度在运行和可运行之间切换;遇到阻塞则进入阻塞状态。

1、New,新建一个线程
2、Runnable,调用了start方法后线程准备就绪,等待获得CPU的使用权。
3、Running,线程正式运行了,执行run中的代码。
4、Blocked,阻塞状态分几种情况:调用wait方法;线程在获取对象的同步锁;调用sleep或join方法;发出IO请求。
5、Dead,线程执行完毕,或者因其他原因而退出了run方法,就结束了生命周期。

加入同步的线程状态图

当资源被一个线程访问时,上锁,其他线程就进入了一个锁池(Lock pool);

当锁被释放,其他线程获得了锁,就变为 可运行(runnable) 状态。

加入线程间的相互作用的线程状态图

线程调用了wait()方法之后,释放掉锁,进入等待池(Wait pool) ;收到通知之后等待获取锁,获取锁之后才可以运行。

线程被阻塞可能是由于下面五方面的原因:

(《Thinking in Java》)

  1.调用sleep(毫秒数),使线程进入睡眠状态。在规定时间内,这个线程是不会运行的。

  2.用suspend()暂停了线程的执行。除非收到resume()消息,否则不会返回“可运行”状态。

  3.用wait()暂停了线程的执行。除非线程收到notify()或notifyAll()消息,否则不会变成“可运行”状态。

  4.线程正在等候一些IO操作完成。

  5.线程试图调用另一个对象的“同步”方法,但那个对象处于锁定状态,暂时无法使用。

参考:

Java 多线程(八) 线程状态图

生产者/消费者问题

wait() / nofity()方法是基类Object的两个方法,也就意味着所有Java类都会拥有这两个方法,这样,我们就可以为任何对象实现同步机制。
wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等等状态,让其他线程执行。
notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。

1 wait方法:
该方法属于Object的方法,wait方法的作用是使得当前调用wait方法所在部分(代码块)的线程停止执行,并释放当前获得的调用wait所在的代码块的锁,并在其他线程调用notify或者notifyAll方法时恢复到竞争锁状态(一旦获得锁就恢复执行)。
调用wait方法需要注意几点:
第一点:wait被调用的时候必须在拥有锁(即synchronized修饰的)的代码块中。
第二点:恢复执行后,从wait的下一条语句开始执行,因而wait方法总是应当在while循环中调用,以免出现恢复执行后继续执行的条件不满足却继续执行的情况。
第三点:若wait方法参数中带时间,则除了notify和notifyAll被调用能激活处于wait状态(等待状态)的线程进入锁竞争外,在其他线程中interrupt它或者参数时间到了之后,该线程也将被激活到竞争状态。
第四点:wait方法被调用的线程必须获得之前执行到wait时释放掉的锁重新获得才能够恢复执行。

2 notify方法和notifyAll方法:
notify方法通知调用了wait方法,但是尚未激活的一个线程进入线程调度队列(即进入锁竞争),注意不是立即执行。并且具体是哪一个线程不能保证。另外一点就是被唤醒的这个线程一定是在等待wait所释放的锁。

3 synchronized关键字:
第一点:synchronized用来标识一个普通方法时,表示一个线程要执行该方法,必须取得该方法所在的对象的锁。
第二点:synchronized用来标识一个静态方法时,表示一个线程要执行该方法,必须获得该方法所在的类的类锁。
第三点:synchronized修饰一个代码块。类似这样:synchronized(obj) { //code…. }。表示一个线程要执行该代码块,必须获得obj的锁。这样做的目的是减小锁的粒度,保证当不同块所需的锁不冲突时不用对整个对象加锁。利用零长度的byte数组对象做obj非常经济。

注意:
LinkedList中的remove方法:
1、remove():移除的是第一项
2、remove(int index):移除的是指定项
3、remove(Object o):移除的是存在的对象

另外,List每remove掉一个元素以后,后面的元素都会向前移动 。(position会变化)。

参考:使用List中remove方法时需要注意的问题

仓库类

import java.util.LinkedList;

/**
 * Created by Administrator on 2017/6/17.
 */
public class Storage {
    // 仓库最大存储量
    private final int MAX_SIZE = 100;

    // 仓库存储的载体
    private LinkedList<Object> list = new LinkedList<Object>();

    // 生产num个产品
    public void produce(int num) {
        // 同步代码段
        synchronized (list) {
            //共享空间满时生产者不能继续生产     生产前循环判断是否为满,满的话将该线程wait,释放锁允许其他同步方法执行
            // 如果仓库剩余容量不足
            while (list.size() + num > MAX_SIZE) {//仓库满了不能再生产
                System.out.println("+++++++++++【要生产的产品数量】:" + num + "/t【库存量】:"
                        + list.size() + "/t暂时不能执行生产任务!");
                try {
                    // 由于条件不满足,生产阻塞
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            // 生产条件满足情况下,生产num个产品
            for (int i = 1; i <= num; ++i) {
                list.add(new Object());
            }

            System.out.println("++【已经生产产品数】:" + num + "/t【现仓储量为】:" + list.size());

            list.notifyAll();
        }
    }

    // 消费num个产品
    public void consume(int num) {
        // 同步代码段
        synchronized (list) {
            //共享空间空时消费者不能继续消费     消费前循环判断是否为空,空的话将该线程wait,释放锁允许其他同步方法执行
            // 如果仓库存储量不足
            while (list.size() < num) {//仓库空了不能再消费
                System.out.println("-------------【要消费的产品数量】:" + num + "/t【库存量】:"
                        + list.size() + "/t暂时不能执行消费任务!");
                try {
                    // 由于条件不满足,消费阻塞
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            // 消费条件满足情况下,消费num个产品
            for (int i = 1; i <= num; ++i) {
                list.remove();//移除第一个
            }

            System.out.println("--【已经消费产品数】:" + num + "/t【现仓储量为】:" + list.size());

            list.notifyAll();
        }
    }

    // get/set方法
    public LinkedList<Object> getList() {
        return list;
    }

    public void setList(LinkedList<Object> list) {
        this.list = list;
    }

    public int getMAX_SIZE() {
        return MAX_SIZE;
    }
}

生产者:

/**
 * Created by Administrator on 2017/6/17.
 */
public class MyProducer extends Thread
{
    // 每次生产的产品数量
    private int num;

    // 所在放置的仓库
    private Storage storage;

    // 构造函数,设置仓库
    public MyProducer(Storage storage)
    {
        this.storage = storage;
    }

    // 线程run函数
    public void run()
    {
        produce(num);
    }

    // 调用仓库Storage的生产函数
    public void produce(int num)
    {
        storage.produce(num);
    }

    // get/set方法
    public int getNum()
    {
        return num;
    }

    public void setNum(int num)
    {
        this.num = num;
    }

    public Storage getStorage()
    {
        return storage;
    }

    public void setStorage(Storage storage)
    {
        this.storage = storage;
    }
}

消费者:

/**
 * Created by Administrator on 2017/6/17.
 */
public class MyConsumer extends Thread
{
    // 每次消费的产品数量
    private int num;

    // 所在放置的仓库
    private Storage storage;

    // 构造函数,设置仓库
    public MyConsumer(Storage storage)
    {
        this.storage = storage;
    }

    // 线程run函数
    public void run()
    {
        consume(num);
    }

    // 调用仓库Storage的生产函数
    public void consume(int num)
    {
        storage.consume(num);
    }

    // get/set方法
    public int getNum()
    {
        return num;
    }

    public void setNum(int num)
    {
        this.num = num;
    }

    public Storage getStorage()
    {
        return storage;
    }

    public void setStorage(Storage storage)
    {
        this.storage = storage;
    }
}

测试类:

/**
 * Created by Administrator on 2017/6/17.
 */
public class Test {
    public static void main(String[] args) {
        // 仓库对象  
        Storage storage = new Storage();

        // 生产者对象  
        MyProducer p1 = new MyProducer(storage);
        MyProducer p2 = new MyProducer(storage);
        MyProducer p3 = new MyProducer(storage);
        MyProducer p4 = new MyProducer(storage);
        MyProducer p5 = new MyProducer(storage);
        MyProducer p6 = new MyProducer(storage);
        MyProducer p7 = new MyProducer(storage);

        // 消费者对象  
        MyConsumer c1 = new MyConsumer(storage);
        MyConsumer c2 = new MyConsumer(storage);
        MyConsumer c3 = new MyConsumer(storage);

        // 设置生产者产品生产数量  
        p1.setNum(10);
        p2.setNum(10);
        p3.setNum(10);
        p4.setNum(10);
        p5.setNum(10);
        p6.setNum(10);
        p7.setNum(80);

        // 设置消费者产品消费数量  
        c1.setNum(50);
        c2.setNum(20);
        c3.setNum(30);

        // 线程开始执行  
        c1.start();
        c2.start();
        c3.start();
        p1.start();
        p2.start();
        p3.start();
        p4.start();
        p5.start();
        p6.start();
        p7.start();
    }
}  

运行结果:

-------------【要消费的产品数量】:50/t【库存量】:0/t暂时不能执行消费任务!
++【已经生产产品数】:10/t【现仓储量为】:10
++【已经生产产品数】:10/t【现仓储量为】:20
-------------【要消费的产品数量】:50/t【库存量】:20/t暂时不能执行消费任务!
++【已经生产产品数】:10/t【现仓储量为】:30
++【已经生产产品数】:10/t【现仓储量为】:40
-------------【要消费的产品数量】:50/t【库存量】:40/t暂时不能执行消费任务!
++【已经生产产品数】:10/t【现仓储量为】:50
--【已经消费产品数】:30/t【现仓储量为】:20
-------------【要消费的产品数量】:50/t【库存量】:20/t暂时不能执行消费任务!
--【已经消费产品数】:20/t【现仓储量为】:0
-------------【要消费的产品数量】:50/t【库存量】:0/t暂时不能执行消费任务!
++【已经生产产品数】:10/t【现仓储量为】:10
-------------【要消费的产品数量】:50/t【库存量】:10/t暂时不能执行消费任务!
++【已经生产产品数】:80/t【现仓储量为】:90
--【已经消费产品数】:50/t【现仓储量为】:40

参考:

生产者/消费者问题的多种Java实现方式

猜你喜欢

转载自blog.csdn.net/sinat_31057219/article/details/73368530