java 并发(三)---Thread 线程

Thread 的状态

      线程共有五种状态.分别是: (1)新建 (2)就绪 (3)运行 (4)阻塞 (5)死亡 ,下面列列举的状态需要结合状态示意图更好理解.

 

  •  新建状态(New): 新创建了一个线程对象。
  • 就绪状态(Runnable): 线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权。即在就绪状态的进程除CPU之外,其它的运行所需资源都已全部获得(包括我们所说的锁)。
  • 运行状态(Running): 就绪状态的线程获取了CPU,执行程序代码。
  • 阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态
    • 等待阻塞 :  运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒.
    • 同步阻塞 :  运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中,言外之意就是锁被其他线程拿了,自己只能等待。
    • 其他阻塞 :  运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
    • 阻塞这个状态可以这样总结: 线程存在且没死亡,那么运行和就绪以外的状态就是阻塞,不管是否获得锁或是进入锁池. 
  • 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

 


     下面为线程的状态示意图:

Thread

     

Thread 方法 和 Object两个方法

      后面两个Object 方法

  • sleep 方法 :  “sleep”—“睡觉”,意思就是休眠一段时间,时间过后继续执行,不释放锁和其他资源,进入阻塞状态
  • join   方法 :   源码实现在下方,可以看到只要thread存活的情况下就会某个时间内循环一个等待的方法 (注意这个方法和下面的

                        wait 方法不是一回事,下面的wait方法会一直阻塞在那里), 使用场景是例如某个操作执行前需要执行一个加载资源的

                        任务,那么执行的这个操作就要一直等待加载的操作完成以后才可以执行(join(0)).

  • yield 方法 :     让步于其他线程执行.
  • wait  方法 :     等待,释放锁和其他资源,进入等待队列,这个等待队列里边存放的对象都是等待获取锁的对象,另外一点, wait 是对象

                         object 中的方法,而不是线程中的方法,同时调用 XX.wait(); 时必须要在同步语句中,或是该对象已经被加锁的情况

                         下,试想一下,wait 方法本身就是某个对象加锁后释放锁,不可能没加锁的情况下可以释放锁.wait 不能自动唤醒,需要

                         notify / notifyAll 方法

  • notify/notifyAll 方法 :  唤醒

   join的源码实现

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() – base;
            }
        }
    }

       但是有个问题一直没搞明白,就是源码中的wait方法,要是Object.wait方法,那么它会释放锁,然后其他线程可以获取锁,执行其他线程的动作,但是在join的源码实现中即使是一直循环,按道理是会释放锁的,其他线程可以执行的,但是事实却不是,如下代码,至今未懂.

Thread thread1 = new Thread(() -> {
            System.out.println("t1 开始执行" + new Date());
            synchronized (obj) {
                try {
                    Thread.currentThread().join(0);
                   //obj.wait();
                    System.out.println("线程1 继续执行,执行完毕");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });


        Thread thread2 = new Thread(() -> {
            try {
                synchronized (obj) {

                    System.out.println("线程2 开始执行 " + new Date());
                    Thread.sleep(2 * 1000);
                    System.out.println("线程2 执行结束 " + new Date());
                  //  obj.notifyAll();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        thread1.start();

        Thread.sleep(4*1000);
        thread2.start();

       执行后会发现线程一调用join()方法后,线程2没能获取对象执行,而是等待线程1执行完成后,线程2才会执行.

 

       我们来看看在 Java 7 Concurrency Cookbook 中相关的描述(很清楚地说明了 join() 的作用):

Waiting for the finalization of a thread

In some situations, we will have to wait for the finalization of a thread. For example, we may have a program that will begin initializing the resources it needs before proceeding with the rest of the execution. We can run the initialization tasks as threads and wait for its finalization before continuing with the rest of the program. For this purpose, we can use the join() method of the Thread class. When we call this method using a thread object, it suspends the execution of the calling thread until the object called finishes its execution.

       当我们调用某个线程的这个方法时,这个方法会挂起调用线程,直到被调用线程结束执行,调用线程才会继续执行。


方法使用 

wait 和 notify 方法

       可以看到 notify 方法的使用,是在同步方法内,并且同样获取同样的锁对象, wait 和 notify 方法的运用常常被用来做生产者-消费者的实现.

//以下代码来自参考文章,见参考资料
public class Test {
    public static Object object = new Object();
    public static void main(String[] args) {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
         
        thread1.start();
         
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
         
        thread2.start();
    }
     
    static class Thread1 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                try {
                    object.wait();
                } catch (InterruptedException e) {
                }
                System.out.println("线程"+Thread.currentThread().getName()+"获取到了锁");
            }
        }
    }
     
    static class Thread2 extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                object.notify();
                System.out.println("线程"+Thread.currentThread().getName()+"调用了object.notify()");
            }
            System.out.println("线程"+Thread.currentThread().getName()+"释放了锁");
        }
    }
}

 

join 方法

       下面代码中parent线程会等待child线程执行完成后再继续执行.

// 父线程
public class Parent extends Thread {
    public void run() {
        Child child = new Child();
        child.start();
        child.join();
        // ...
    }
}
// 子线程
public class Child extends Thread {
    public void run() {
        // ...
    }
}

yield

public class YieldExcemple {

    public static void main(String[] args) {
        Thread threada = new ThreadA();
        Thread threadb = new ThreadB();
        // 设置优先级:MIN_PRIORITY最低优先级1;NORM_PRIORITY普通优先级5;MAX_PRIORITY最高优先级10
        threada.setPriority(Thread.MIN_PRIORITY);
        threadb.setPriority(Thread.MAX_PRIORITY);

        threada.start();
        threadb.start();
    }
}

class ThreadA extends Thread {
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("ThreadA--" + i);
            Thread.yield();
        }
    }
}

class ThreadB extends Thread {
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("ThreadB--" + i);
            Thread.yield();
        }
    }
}

          以下总结来自参考文章.

       Java线程中的Thread.yield( )方法,译为线程让步。顾名思义,就是说当一个线程使用了这个方法之后,它就会把自己CPU执行的时间让掉,让自己或者其它的线程运行,注意是让自己或者其他线程运行,并不是单纯的让给其他线程。

        yield()的作用是让步。它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是当前线程又进入到“运行状态”继续运行!

      举个例子:一帮朋友在排队上公交车,轮到Yield的时候,他突然说:我不想先上去了,咱们大家来竞赛上公交车。然后所有人就一块冲向公交车,

有可能是其他人先上车了,也有可能是Yield先上车了。

     但是线程是有优先级的,优先级越高的人,就一定能第一个上车吗?这是不一定的,优先级高的人仅仅只是第一个上车的概率大了一点而已,

     最终第一个上车的,也有可能是优先级最低的人。并且所谓的优先级执行,是在大量执行次数中才能体现出来的。

参考资料:

猜你喜欢

转载自www.cnblogs.com/Benjious/p/10053610.html