• 144810

    文章

  • 854

    评论

  • 13

    友链

  • 最近新加了换肤功能,大家多来逛逛吧~~~~
  • 喜欢这个网站的朋友可以加一下QQ群,我们一起交流技术。

线程的安全问题


线程安全问题:

问题:当某个线程操作共享数据中,尚未操作完成时,其他线程参与进来,也操作共享数据。

解决方法:当一个线程在操作共享数据时,其他线程不能参与进来。即使操作中的线程出现阻塞,也不能改变。

例子:创建三个窗口卖票,总票数为100张,使用实现Runable接口的方式。

JAVA 中通过同步机制 来解决线程的安全问题。

方式一、同步代码块

 synchronized(同步监视器){

    //需要被同步的代码   

说明:

1.操作共享数据的代码,为需要被同步的代码

2.共享数据,多个线程共同操作的变量,

3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。

    要求:多个线程必须要共用同一把锁。

 补充:在实现Runable接口创建多线程的方式中,我们可以考虑使用this 充当同步监视器

使用实现 Runable方式:

public class TheardWindow {
    public static void main(String[] args) {
        TicketWindow w1 = new TicketWindow();
        Thread t1=new Thread(w1);
        Thread t2= new Thread(w1);
        Thread t3 =new Thread(w1);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();;
        t2.start();
        t3.start();

    }
}
class TicketWindow implements Runnable{
    private  int ticket=100;
    final Object obj=new Object();

    @Override
    public void run() {
        while (true){
            //synchronized (obj){
            synchronized (this){
                if (ticket>0){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":卖票 票号:"+ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }
}

使用继承方式: 

package org.zhanghl;

public class WindowB {
    public static void main(String[] args) {
        TicketRun t1= new TicketRun();
        TicketRun t2= new TicketRun();
        TicketRun t3 =new TicketRun();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

class TicketRun extends Thread{
    private static int ticket=100;
    private static Object obj=new Object();
    @Override
    public void run() {
        while (true){
            //synchronized (obj){
            //可以用当前类做锁
            synchronized (WindowB.class){
                if (ticket>0){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":卖票 票号:"+ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }
}

 

使用同步代码块 解决继承Thread类的方式的线程安全问题

注意:

1. 共享数据与锁对象都应是唯一的,这里为static.

2. 用继承Thread类创建多线程的方式时,慎用this充当同步监视器,考虑使用当前类对象做锁。Class只会加载一次。

3. 同步的方式,优点是解决了线程的安全问题,

    缺点是操作同步代码时,只能由一个线程参与,其他线程等待。先当与是一个线程的过程,效率低。

 

方式二、同步方法

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

package org.zhanghl;

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

class WindowC1 implements Runnable {
    private int ticket = 100;
    final Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            if (ticket > 0) {
                show();
            } else {
                break;
            }
        }
    }

    /*同步监视器:this */
    private synchronized void show() {
        if (ticket > 0) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":卖票 票号:" + ticket);
            ticket--;
        }
    }
}
/*
*使用同步方法处理继承Thread类的方式中的线程安全问题
* */
class WindowC2 extends Thread{
    private static int ticket = 100;
    final Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            if (ticket > 0) {
                show();
            } else {
                break;
            }
        }
    }

    /*同步监视器:this */
    private static synchronized void show() { /*此时同步监视器是 WindowC2.class*/
        //private synchronized void show() 此种方式是错误的
        if (ticket > 0) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":卖票 票号:" + ticket);
            ticket--;
        }
    }
}
public class WindowC {
    public static void impl_show(){
        WindowC1 w1 = new WindowC1();
        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }

    public static  void extd_show(){
        WindowC2 t1=new WindowC2();
        WindowC2 t2=new WindowC2();
        WindowC2 t3=new WindowC2();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
    public static void main(String[] args) {
        /*实现方式,同步方法*/
       //impl_show();
        /*继承方式,同步方法*/
        extd_show();
    }
}

 

总结:

1.同步方法仍然涉及到同步监视器,只是不需要我们显示的声明。

2.非静态的同步方法,同步监视器是this.

静态的同步方法,同步监视器是 当前类本身。

 

 

 


695856371Web网页设计师②群 | 喜欢本站的朋友可以收藏本站,或者加入我们大家一起来交流技术!

0条评论

Loading...


发表评论

电子邮件地址不会被公开。 必填项已用*标注

自定义皮肤 主体内容背景
打开支付宝扫码付款购买视频教程
遇到问题联系客服QQ:419400980
注册梁钟霖个人博客