多线程的安全问题一:同步方法与同步代码块

一.引出线程安全问题

        多个线程操作共享数据时,如果线程不完整,则很可能出现线程安全问题,线程执行不确定导致结果不确定.

        这里有一个三窗口买票程序,共100张票

public class Main2 {
    public static void main(String[] args) {

        myThread2 m = new myThread2();

        //设置窗口
        Thread t1 = new Thread(m);
        t1.setName("窗口1");
        Thread t2 = new Thread(m);
        t2.setName("窗口2");
        Thread t3 = new Thread(m);
        t3.setName("窗口3");

        //开始卖票
        t1.start();
        t2.start();
        t3.start();
    }
}
class myThread2 implements Runnable{
    //多窗口买票
    private int ticket = 100;

    @Override
    public void run() {
        while(ticket>=0){//若有余票则继续

            try {
                //线程睡眠,但不释放锁,其他线程无法执行
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"卖票,剩余票数:"+ticket);//卖票
            ticket--;
        }
    }
}

sleep()的目的是加上后线程会阻塞,操作未完成,其他线程参与进来的概率增加

以上运行结果会出现错票问题,如余票为-2的情况

解决方式一:同步代码块(锁)

1.Runnable解决方法

使用同步代码块将共享数据(ticket)所在的地方包起来

线程操作未完成时,锁不打开,其他线程无法参与进来

使用关键字

synchronized (同步监视器){....}

任何一个对象都可以充当任务监视器

一般在run()方法外定义对象(不能直接创建)

传到同步代码块中

多线程必须使用同一把锁(Object对象)

class myThread2 implements Runnable{
    //多窗口买票
    private int ticket = 100;//只能
    Object object = new Object();
    @Override
    public void run() {
        synchronized (object) {//同步代码块
            while (ticket >= 0) {//若有余票则继续

                try {
                    //线程睡眠,但不释放锁,其他线程无法执行
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(
                Thread.currentThread().getName() + "卖票,剩余票数:" + ticket);//卖票
                ticket--;
            }
        }
    }
}

2.继承Thread类解决方法

如果用刚才的同步代码块解决继承Thread类方式卖票的方式肯定是不行的

因为此方式多窗口卖票要创造三个对象,不能共用同一把锁

那么解决方式就是将Object类的对象设置为静态,这样就共享同一个对象了

public class Main {
    public static void main(String[] args) {
        MyThread m1 = new MyThread();
        m1.setName("一号窗口");
        MyThread m2 = new MyThread();
        m2.setName("二号窗口");
        MyThread m3 = new MyThread();
        m3.setName("三号窗口");

        m1.start();
        m2.start();
        m3.start();

    }
}
class MyThread extends Thread{
    private static int ticket = 100;
    private static Object object = new Object();
    @Override
    public void run() {
        synchronized (object) {
            while (ticket>=0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(
                Thread.currentThread().getName() + "卖票,剩余票数:" + ticket);//卖票
                ticket--;
            }
        }
    }
}

还有一种方式就是考虑使用当前类作为同步监视器

        synchronized (MyThread.class) {}

类也是一种对象

解决方式二:同步方法

同步代码块看着有点别扭,如果操作都在一个方法中那就可以直接声明该方法了

class myThread2 implements Runnable{
    //多窗口买票
    private int ticket = 100;//只能
    Object object = new Object();
    @Override
    public void run() {
        synchronized (object) {//同步代码块
            while (ticket >= 0) {//若有余票则继续
                show();
            }
        }
    }
    private synchronized void show(){//声明同步方法
        if(ticket>=0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(
            Thread.currentThread().getName() + "卖票,剩余票数:" + ticket);//卖票
            ticket--;
        }
    }
}

这个时候同步监视器就是this了

这是实现Runnable类的解决方式,

那么继承Thread的解决方式则是在该show方法前面加上static就行

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值