线程的生命周期,同步,和通信

一:线程的生命周期

1.Thread.State类

Thread.State类定义了线程的几种状态,在一个完整的生命周期中通常要经历五种状态:

  1. 新建: 当一个Thread类或其子类的对象被声明并创建时
  2. 就绪: start()方法后,将进入线程队列等待CPU时间片
  3. 运行: 当就绪的线程被调度并获得CPU资源时,便进入运行状态
  4. 阻塞: 被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的执行,进入阻塞状态
  5. 死亡: 线程完成全部工作或被提前强制性终止或出现异常导致结束

2.线程状态转换图

在这里插入图片描述

二:线程的同步

1.多线程的安全问题

  1. 问题原有: 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,另一个线程就参与进来执行,导致共享数据的错误.
  2. 解决办法: 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行.

2.三种解决方法

2.1.同步代码块
synchronized(对象){
  // 需要被同步的代码;
}

说明:

  1. 操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。
  2. 共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。
  3. 同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
    要求:多个线程必须要共用同一把锁。

补充:

  • 在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。
  • 在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
2.2.同步方法
public synchronized void add (String id){
  ......
}

如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。

  1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
  2. 非静态的同步方法,同步监视器是:this
    静态的同步方法,同步监视器是:当前类本身

同步的方式,解决了线程的安全问题。—好处
操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。 —局限性

2.3.示例
/**
 * 示例:创建三个窗口卖票,总票数为100张.采用继承方式
 * 1.问题:卖票过程中,出现了重票、错票 -->出现了线程的安全问题
 * 2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。
 * 3.如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,其他
 *            线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。
 */
 
//方式一:采用继承类的方式
class Window1 extends Thread{
    // 共享资源需要加上static
    private static int ticket = 100;

    @Override
    public void run() {
        while (true){
            //同步方法
//            handleTicket();

            // 同步代码块
            // 不能使用this,this代表w1,w2,w3
            synchronized (Window1.class) {   //Class clazz = Window1.class,Window1.class只会加载一次
                if (ticket > 0){
                    try {
                        sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName()+":"+ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }

    // 方式一
    /*private void handleTicket() {
        // 同步代码块
        synchronized (Window.class) {   //Class clazz = Window.class
            if (ticket > 0){
                System.out.println(getName()+":"+ticket);
                ticket--;
            }
        }
    }*/
    // 方式二
    // 同步方法: 需要加上static,保证是同一个对象锁
    // 方法不加static时,同步监视器:w1,w2,w3
    private static synchronized void handleTicket() {   // 同步监视器:Window.class
        if (ticket > 0){
            try {
                //sleep()方法属于静态方法,与对象无关
                sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // static方法内容不能使用this,super关键字
            System.out.println(Thread.currentThread().getName()+":"+ticket);
            ticket--;
        }
    }
}

public class ExtendsTest {
    public static void main(String[] args) {
        Window1 w1 = new Window1();
        Window1 w2 = new Window1();
        Window1 w3 = new Window1();

        w1.setName("窗口一");
        w2.setName("窗口二");
        w3.setName("窗口三");

        w1.start();
        w2.start();
        w3.start();
    }
}


//方式二:采用实现接口的方式
class Window2 implements Runnable{
    // 不需要加static
    private int ticket = 100;

    @Override
    public void run() {
        while (true){
            //同步方法
//            handleTicket();

            //同步代码块
            synchronized (this) {   //此时的this:唯一的Window2的对象
                if(ticket > 0){
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // getName()属于Thread的方法
                    System.out.println(Thread.currentThread().getName()+":"+ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }

    //方式一
    /*private void handleTicket() {
        // 同步代码块
        synchronized (this) {   //同步监视器:this
            if(ticket > 0){
                System.out.println(Thread.currentThread().getName()+":"+ticket);
                ticket--;
            }
        }
    }*/
    //方式二
    //同步方法: 不需要加static
    private synchronized void handleTicket() {  //同步监视器:this
        if(ticket > 0){
            System.out.println(Thread.currentThread().getName()+":"+ticket);
            ticket--;
        }
    }
}

public class ImplementTest {
    public static void main(String[] args) {
        Window2 w1 = new Window2();

        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        t1.start();
        t2.start();
        t3.start();
    }
}
2.4.lock
  1. synchronized 与 Lock的异同?

    相同:二者都可以解决线程安全问题
    不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
    Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())

  2. 优先使用顺序:

    Lock > 同步代码块(已经进入了方法体,分配了相应资源)> 同步方法(在方法体之外)

  3. 代码:

class Window implements Runnable{
    private int ticket = 100;
    //1.实例化ReentrantLock
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while(true){
            try{
                //2.调用锁定方法lock()
                lock.lock();

                if(ticket > 0){

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }finally {
                //3.调用解锁方法:unlock()
                lock.unlock();
            }
        }
    }
}

public class LockTest {
    public static void main(String[] args) {
        Window w = new Window();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

三:线程的通信

1.涉及到的三个方法

  1. wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
  2. notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
  3. notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。

2.说明

  1. wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
  2. wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则,会出现IllegalMonitorStateException异常.
  3. wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。

3.sleep() 和 wait()的异同?

  1. 相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
  2. 不同点:
    1. 两个方法声明的位置不同:Thread类中声明sleep() , Object类中声明wait()
    2. 调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中
    3. 关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁

4.示例

class Number implements Runnable{
    private int number = 1;
    private Object obj = new Object();
    @Override
    public void run() {
        while(true){

            synchronized (obj) {
                obj.notify();

                if(number <= 100){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;

                    try {
                        //使得调用如下wait()方法的线程进入阻塞状态
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }else{
                    break;
                }
            }
        }
    }
}

public class CommunicationTest {
    public static void main(String[] args) {
        Number number = new Number();
        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);

        t1.setName("线程1");
        t2.setName("线程2");

        t1.start();
        t2.start();
    }
}
发布了91 篇原创文章 · 获赞 2 · 访问量 2424

猜你喜欢

转载自blog.csdn.net/zhixingwu/article/details/103226119
今日推荐