java基础---多线程---线程的几种状态及其转换,wait,notify,sleep,yield,join

时间:2023-02-14 20:23:35
 
 
 
----线程的几种状态:新建状态,就绪状态,阻塞状态,运行状态,死亡状态。b几种状态之间如何转换。wait,notify,yield,sleep,join
一、线程的状态
   Java中线程中状态可分为五种:New(新建状态),Runnable(就绪状态),Running(运行状态),Blocked(阻塞状态),Dead(死亡状态)。
  New:新建状态,当线程创建完成时为新建状态,即new Thread(...),还没有调用start方法时,线程处于新建状态。
  Runnable:就绪状态,当调用线程的的start方法后,线程进入就绪状态,等待CPU资源。处于就绪状态的线程由Java运行时系统的线程调度程序(thread scheduler)来调度。
  Running:运行状态,就绪状态的线程获取到CPU执行权以后进入运行状态,开始执行run方法。
  Blocked:阻塞状态,线程没有执行完,由于某种原因(如,I/O操作等)让出CPU执行权,自身进入阻塞状态。
  Dead:死亡状态,线程执行完成或者执行过程中出现异常,线程就会进入死亡状态。
  这五种状态之间的转换关系如下图所示:
  java基础---多线程---线程的几种状态及其转换,wait,notify,sleep,yield,join 
  有了对这五种状态的基本了解,现在我们来看看Java中是如何实现这几种状态的转换的。 
 
就绪状态:就缺cpu
阻塞状态:因为其他条件而放弃cpu,缺cpu的同时缺其他条件(比如锁,比如要sleep休眠,比如io阻塞等待输入)
 
 

wait(timeout)释放锁的同时也释放了cpu资源,线程进入阻塞状态。
notify提醒线程获取锁,进入可执行状态。
sleep方法不会释放锁,但是会释放cpu资源,也是进入阻塞状态,因为要休眠。
yield方法从running到runnable进入可运行状态,可以让同优先级的线程获取cpu资源。
join方法将并发执行的线程变成同步执行。父线程等待子线程执行完毕,调用join的线程会先执行完。
 
sleep()方法的用处
1.让使用这个方法的线程在指定时间内不执行
2.不释放资源锁,相当于一直拿着这个锁,其他同步方法不能执行。
3.可以使得优先级低的线程得到运行的机会。
===?如何理解windows是抢占式操作系统?
1.抢占式操作系统会将cpu交给优先级最高的线程。
2.sleep(5000)函数的作用是告诉操作系统接下来5秒不参与cpu竞争,5秒后再根据优先级排序进行cpu的竞争
3.sleep(0)的意思是当前线程告诉操作系统做一下优先级排序。
 
 
yield()方法的用处
将cpu交给相同优先级的线程,自身进入可运行的runnable状态。
1.使得当前线程返回可执行的状态,就是当前线程在执行,通过调用yield()之后,线程进入等待状态。
2.使得同等级或者比当前线程等级高的线程获得执行的机会。
  • Yield是一个静态的原生(native)方法
  • Yield告诉当前正在执行的线程把运行机会交给线程池中拥有相同优先级的线程。
  • Yield不能保证使得当前正在运行的线程迅速转换到可运行的状态
  • 它仅能使一个线程从运行状态转到可运行状态,而不是等待或阻塞状态
是object的方法。
某个线程获取了锁,调用wait()方法释放锁,进入等待状态。
另一个线程获取锁,调用Notify()后执行完同步代码然后释放锁,唤醒等待的线程。
如果不在同步代码块中调用wait和notify就会抛出异常。

 
1.公共资源Mylist
2.线程a,b
3.run主函数
 
结论
1.wait()方法能够在调用的时候立刻释放掉锁,同时使得当前的线程进入等待唤醒的状态。
2.notify()方法能够唤醒一个,只能是一个等待状态的线程,但是这个线程不是立马获得锁,而是还要排队去竞争这个锁。notify()不会立刻释放掉锁,必须执行完同步代码块才会释放锁,同步代码块指的是synchronized(lock)里面的代码部分
3.在执行同步代码块的时候,如果遇到异常也会释放锁。就是一个线程阻塞了这个时候interrupt()就会抛出异常,所以为什么catch的异常都是InterruptedException e.
4.同步的方法除了使用synchronized锁住监视器外,还可以通过给方法添加synchronized关键字对方法进行同步。
5.wait()和notify()以及notifyall()方法都必须写在同步代码块区域中,否则会出现illegalMonitorStateException的异常。
 
======================================================================
 
===>使用饿汉式单例模式创建公共使用的对象
public class MyList {
    static private List list = new ArrayList();
    static public void add(){
        list.add("anything");
    }
    static public int size(){
        return list.size();
    }
}
 
===》线程A使用接口Runnable来实现线程
1.使用Runnable,说明ThreadA只是一个线程实例,那么真正运行的时候外面必须包装一层Thread,所以:Thread thread = new Thread(new ThreadA());
2.logger使用static的方式来创建使用
3.lock对象充当监视器!!!lock在不同的线程中可以同时使用,所以必须对lock进行同步。同时lock对象是Object对象,实现了wait()和notify()函数,wait()函数能够使得当前的线程进入等待状态,notify()函数能够唤醒一个因为调用了wait()而进入阻塞状态的线程。
4.sleep(),wait()都能够使得线程进入阻塞。同时wait()函数能够释放掉锁,但是sleep()并不会释放掉锁。
public class ThreadA implements Runnable {
    static private Logger logger;
    private Object lock;
 
    static{
        if(LoggerContext.logger == null){
            LoggerContext.initLogger();
        }
        logger = LoggerFactory.getLogger(ThreadA.class);
    }
 
    public ThreadA(Object lock){
        super();
        this.lock = lock;
    }
 
    @Override
    public void run() {
        logger.debug("ThreadA run()");
        try{
            synchronized (lock){
                if(MyList.size() != 5){
                    System.out.println("ThreadA wait begin"
                            + System.currentTimeMillis());
                    lock.wait();
                    System.out.println("ThreadA wait end"
                            + System.currentTimeMillis());
                }
            }
        }catch(InterruptedException e){
            logger.error("lock.wait() error",e);
        }
    }
}
 
 
===》线程B使用接口Runnable来实现线程
1.lock对象调用了notify()函数,使得处于等待的状态唤醒。注意这里只能够唤醒一个线程。
2.调用了notify()函数不会释放掉锁,必须执行完整个代码块才会释放这个锁。
public class ThreadB implements Runnable {
    static private Logger logger;
    private Object lock;
 
    public ThreadB(Object lock){
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized(lock){
            for(int i=0;i<10;i++){
                MyList.add();
                System.out.println("ThreadB add No." + i + " number");
                if(i==5){
                    lock.notify();
                    System.out.println("发出通知唤醒wait()的线程!!1");
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
 
 
===》run()函数
1.注意要使用线程去运行线程实例。
public class Run {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        ThreadA threadA = new ThreadA(lock);
        ThreadB threadB = new ThreadB(lock);
        Thread thread_01 = new Thread(threadA);
        Thread thread_02 = new Thread(threadB);
        thread_01.start();
        Thread.sleep(1000);
        thread_02.start();
    }
}
 
 
===?什么时候出现java.lang.IllegalMonitorStateException的异常呢?
1>当前线程不含有当前对象的锁资源的时候,调用obj.wait()方法;
2>当前线程不含有当前对象的锁资源的时候,调用obj.notify()方法。
3>当前线程不含有当前对象的锁资源的时候,调用obj.notifyAll()方法。
 
所以必须在同步的代码块中进行wait()和notify()的操作。