Nothing is impossible!

Java 并发编程3--线程优先级及线程的状态

Posted on By zhaofuchao

1.线程的优先级

每个线程都具有各自的优先级,线程的优先级可以在程序中表明该线程的重要性,如果有很多线程处于就绪状态,系统会根据优先级来决定首先使哪个线程进入运行状态。但这个并不意味着低优先级的线程得不到运行,而只是它运行的几率比较小,如垃圾回收机制线程的优先级就比较低。所以很多垃圾得不到及时的回收处理。

线程优先级具有继承特性比如A线程启动B线程,则B线程的优先级和A是一样的。

线程优先级具有随机性也就是说线程优先级高的不一定每一次都先执行完。

Thread类中包含的成员变量代表了线程的某些优先级。

  • 如:
  • Thread.MIN_PRIORITY(常数1),
  • Thread.NORM_PRIORITY(常数5),
  • Thread.MAX_PRIORITY(常数10)。

其中每个线程的优先级都在Thread.MIN_PRIORITY(常数1) 到Thread.MAX_PRIORITY(常数10) 之间,在默认情况下优先级都是Thread.NORM_PRIORITY(常数5)。

学过操作系统这门课程的话,我们可以发现多线程优先级或多或少借鉴了操作系统对进程的管理。

线程优先级具有继承特性测试代码:

public class MyThread1 extends Thread {
	@Override
	public void run() {
		System.out.println("MyThread1 run priority=" + this.getPriority());
		MyThread2 thread2 = new MyThread2();
		thread2.start();
	}
}
public class MyThread2 extends Thread {
	@Override
	public void run() {
		System.out.println("MyThread2 run priority=" + this.getPriority());
	}
}
public class Run {
	public static void main(String[] args) {
		System.out.println("main thread begin priority="
				+ Thread.currentThread().getPriority());
		Thread.currentThread().setPriority(6);
		System.out.println("main thread end   priority="
				+ Thread.currentThread().getPriority());
		MyThread1 thread1 = new MyThread1();
		thread1.start();
	}
}

2.java线程六种状态–线程的生命周期

    1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
    1. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。 线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
  • 3.阻塞(BLOCKED):表示线程阻塞于锁。
  • 4.等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
  • 5.超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
    1. 终止(TERMINATED):表示该线程已经执行完毕。

线程的状态图

2.1 初始状态(NEW)

实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。

MyThread thread = new MyThread();
System.out.println(thread.getState());

2.2运行状态 (RUNNABLE)

这个状态的线程,其正在JVM中执行,但是这个”执行”,不一定是真的在运行, 也有可能是在等待CPU资源。所以,在网上,有人把这个状态区分为READY和RUNNING两个,一个表示的start了,资源一到位随时可以执行,另一个表示真正的执行中的

就绪状态(READY)

  • 就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态。
  • 调用线程的start()方法,此线程进入就绪状态。
  • 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
  • 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
  • 锁池里的线程拿到对象锁后,进入就绪状态。
    MyThread thread = new MyThread(lock);
          thread.start();
          System.out.println(thread.getState());
    

运行中状态(RUNNING)

线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。

2.3. 阻塞状态(BLOCKED)

阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。

public class MyThread extends Thread {
    private byte[] lock = new byte[0];

    public MyThread(byte[] lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized (lock){
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("done");

        }
    }
}
public static void main(String[] args) throws InterruptedException {
        byte[] lock = new byte[0];
        MyThread thread1 = new MyThread(lock);
        thread1.start();
        MyThread thread2 = new MyThread(lock);
        thread2.start();
        Thread.sleep(1000);//等一会再检查状态
        System.out.println(thread2.getState());
    }

此时我们看到的输出的第二个线程的状态就是BLOCKED

2.4. 等待(WAITING)

处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。 一个线程会进入这个状态,一定是执行了如下的一些代码,例如

Object.wait()
Thread.join()
LockSupport.park()

当一个线程执行了Object.wait()的时候,它一定在等待另一个线程执行Object.notify()或者Object.notifyAll()。

或者一个线程thread,其在主线程中被执行了thread.join()的时候,主线程即会等待该线程执行完成。当一个线程执行了LockSupport.park()的时候,其在等待执行。

Object.wait()

public class MyThread extends Thread {
    private byte[] lock = new byte[0];

    public MyThread(byte[] lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized (lock){
            try {
                lock.wait(); //wait并允许其他线程同步lock
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public static void main(String[] args)
        throws InterruptedException {
        byte[] lock = new byte[0];
        MyThread thread1 = new MyThread(lock);
        thread1.start();
        Thread.sleep(100);
        System.out.println(thread1.getState()); //这时候线程状态应为WAITING
        synchronized (lock){
            lock.notify(); //notify通知其他wait的线程
        }
        Thread.sleep(100);
        System.out.println(thread1.getState());
    }

//输出:WAITING和TERMINATED

Thread.join()

public class MyThread extends Thread {
    private byte[] lock = new byte[0];

    public MyThread(byte[] lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class MyThread1 extends Thread {

    Thread thread;

    public MyThread1(Thread thread) {
        this.thread = thread;
    }

    @Override
    public void run() {
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class Main {

    public static void main(String[] args)
        throws InterruptedException {
        byte[] lock = new byte[0];
        MyThread thread = new MyThread(lock);
        thread.start();
        MyThread1 thread1 = new MyThread1(thread);
        thread1.start();
        Thread.sleep(100);
        System.out.println(thread1.getState());
    }
}

//输出为:WAITING

LockSupport.park()

public class MyThread extends Thread {
    private byte[] lock = new byte[0];

    public MyThread(byte[] lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        LockSupport.park();
    }
}
 public static void main(String[] args) throws InterruptedException {
        byte[] lock = new byte[0];
        MyThread thread1 = new MyThread(lock);
        thread1.start();
        Thread.sleep(100);
        System.out.println(thread1.getState());
        LockSupport.unpark(thread1);
        Thread.sleep(100);
        System.out.println(thread1.getState());
    }
//输出WAITING和TERMINATED

2.5. 超时等待(TIMED_WAITING)

处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。

这个状态和WAITING状态的区别就是,这个状态的等待是有一定时效的,即可以理解为WAITING状态等待的时间是永久的,即必须等到某个条件符合才能继续往下走,否则线程不会被唤醒。但是TIMED_WAITING,等待一段时间之后,会唤醒线程去重新获取锁。当执行如下代码的时候,对应的线程会进入到TIMED_WAITING状态

Thread.sleep(long)
Object.wait(long)
Thread.join(long)
LockSupport.parkNanos()
LockSupport.parkUntil()

Thread.sleep


public class MyThread3 extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  Thread thread = new MyThread3();
        thread.start();
        Thread.sleep(100);
        System.out.println(thread.getState());

输出为TIMED_WAITING

Object.wait

public class MyThread4 extends Thread {
    private Object lock;

    public MyThread4(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {

        synchronized (lock){
            try {
                lock.wait(1000);//注意,此处1s之后线程醒来,会重新尝试去获取锁,如果拿不到,后面的代码也不执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("lock end");
        }
    }
}
byte[] lock = new byte[0];
        MyThread4 thread = new MyThread4(lock);
        thread.start();
        Thread.sleep(100);
        System.out.println(thread.getState());
        Thread.sleep(2000);
        System.out.println(thread.getState());

输出
TIMED_WAITING
lock end
TERMINATED

2.6. 终止状态(TERMINATED)

    1. 当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。
    1. 在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

几个方法的比较

  • Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。
  • Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
  • t.join()/t.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程进入就绪状态。
  • obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
  • obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。