JAVA线程的生命周期和安全问题

导读:本篇文章讲解 JAVA线程的生命周期和安全问题,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

线程的生命周期

  • 新建

当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态

  • 就绪

处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源

  • 运行

当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能

  • 阻塞

在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态

  • 死亡

线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束

JAVA线程的生命周期和安全问题 

 线程的安全问题

/**
 * 1.卖票过程中出现重票和错票----线程的安全问题
 * 2.问题出现的原因---当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票
 * 3.如何解决--加锁;当一个线程在操作ticket过程中,其他线程不能参与进来。直到线程1操作完了之后其他线程才可以参与进来
 * 4.在JAVA中,我们通过同步机制解决线程的安全问题
 * 5.这个程序会出现重票的问题
 */
class Window1 implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {
        while(true){
            if(ticket > 0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();        //会提高出现0或者-1的概率
                }

                System.out.println(Thread.currentThread().getName()+",卖票,票号为:"+ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}
class WindowTest1 {
    public static void main(String[] args) {
        Window1 w = new Window1();

        Thread t1 = new Thread(w);//线程是new出来的同一个对象再次建出来的,所以ticket不用再用static修饰
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

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

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

同步代码块

synchronized(同步监视器){
    //需要被同步的代码
}
//操作共享数据的代码即为需要被同步的代码
//共享数据:多个线程共同操作的变量
//同步监视器:俗称锁。    任何一个类的对象都可以充当锁
//多个线程必须共用同一把锁

在实现Runnable接口创建多线程的方式中,我们可以考虑使用this

//同步的方式,解决了线程的安全问题--好处
//操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低    --局限性
/* 1.卖票过程中出现重票和错票----线程的安全问题
 * 2.问题出现的原因---当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票
 */
class Window1 implements Runnable{
    private int ticket = 100;
    Object obj = new Object();

    @Override
    public void run() {
        while(true){    //如果包在这个前面,那么就变成一个窗口售票
            synchronized(obj) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();        //会提高出现0或者-1的概率
                    }

                    System.out.println(Thread.currentThread().getName() + ",卖票,票号为:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}
class WindowTest1 {
    public static void main(String[] args) {
        Window1 w = new Window1();

        Thread t1 = new Thread(w);//线程是new出来的同一个对象再次建出来的,所以ticket不用再用static修饰
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

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

        t1.start();
        t2.start();
        t3.start();
    }
}
class Window2 extends Thread{

    private static int ticket = 100;

    private static Object obj = new Object();

    @Override
    public void run() {

        while(true){
            //正确的
//            synchronized (obj){       //如果是实现Runnable接口,则这个正确;因为这个时候锁不唯一;
//                                      下边Window2实例化了三个对象,需要用static对obj进行修饰
            synchronized (Window2.class){//Class clazz = Window2.class,Window2.class只会加载一次
                //错误的方式:this代表着t1,t2,t3三个对象
//              synchronized (this){        只有在实现Runnable接口中,Window只实例化了一个对象,可以使用this

                if(ticket > 0){

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

                    System.out.println(getName() + ":卖票,票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }

        }

    }
}


public class WindowTest2 {
    public static void main(String[] args) {
        Window2 t1 = new Window2();
        Window2 t2 = new Window2();
        Window2 t3 = new Window2();


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

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

    }
}

同步方法

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

使用同步方法解决实现Runnable接口的线程安全问题

/**
 * 使用同步方法解决实现Runnable接口的线程安全问题
 *
 *
 *  关于同步方法的总结:
 *  1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
 *  2. 非静态的同步方法,同步监视器是:this
 *     静态的同步方法,同步监视器是:当前类本身
 *
 */

class Window3 implements Runnable {

    private int ticket = 100;

    @Override
    public void run() {
        while (true) {
            show();
        }
    }

    private synchronized void show(){   //同步监视器:this
        //synchronized (this){

        if (ticket > 0) {

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

            System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);

            ticket--;
        }
        //}
    }
}


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

        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();
    }

}
class Window4 extends Thread{

    private static int ticket = 100;

    private static Object obj = new Object();

    @Override
    public void run() {

        while (true) {
            show();
        }
    }
    //private synchronized void show() {  //因为同步监视器为this,现在的对象不是一个,所以不行
    private static synchronized void show() {   //同步监视器为Window4.class
        if (ticket > 0) {

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

            System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
            ticket--;
        }
    }
}


public class WindowTest4 {
    public static void main(String[] args) {
        Window4 t1 = new Window4();
        Window4 t2 = new Window4();
        Window4 t3 = new Window4();

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

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

    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/83244.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!