山月记

苟有恒何必三更眠五更起 最无益莫过一日曝十日寒

认识java中线程安全问题

出现场景: 多个线程同时操作一个对象,如果该对象没有线程安全的控制,便会出现线程安全问题。例如:我们有一个类A

public class A{
    int count=0;
    public void add1000(){
        for(int i=0;i<1000;i++){
            count++;
            System.out.println(count);
        }

    }
}

如上代码所示,A中有一个成员变量count,有一个让count加1000的方法,并打印。如果声明A一个对象,一个线程访问没问题,会从打印0~999。但当多个线程操作A的同一个对象,就可能发生一个现象:

public static void main(String[] args) {

        Run run = new Run();
        new Thread(new Runnable() {

            @Override
            public void run() {
                run.add1000("A");
            }

        }).start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                run.add1000("B");
            }

        }).start();

    }

从上面的代码可以看到,启动了两个线程,每个线程都只是想让A的count打印从0到999,但由于线程之间的运行方式是轮着运行的(对线程运行方法不了解,可自行搜索学习),会导致这两个线程都操作count,count的值也是在一直变的,就不能保证add1000这个方法的原子性。

解决办法: 对于解决办法,java特别提供了保证线程安全的类和关键字,同时java的类中也有好多是线程安全和不安全两个版本。
首先最简单的解决办法就是使用synchronized关键字

public synchronized void add1000(String s) {
            for (int i = 0; i < 1000; i++) {
                count++;
                System.out.println(s + count);
            }

        }

在add1000方法前添加synchronized关键字便可保证该方法变成线程安全的,原理大概是,当某个线程调用此方法时,会获取该实例的对象锁,方法未结束之前,其他线程只能去等待。当这个方法执行完时,才会释放对象锁。其他线程才有机会去抢占这把锁,去执行该方法。同时该关键字也能同步代码块。

使用Lock类;lock是java concurrent包下的一个类,该类可以很自由的给任意代码段添加锁及释放锁。

public static void main(String[] args) {


        Lock lock = new ReentrantLock();
        Run run = new Run(lock);
        new Thread(new Runnable() {

            @Override
            public void run() {
                run.add1000("A");
            }

        }).start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                run.add1000("B");
            }

        }).start();

    }

    public static  class Run {
        int count = 0;
        Lock lock;
        public Run(Lock lock){
            this.lock=lock;
        }

        // count加1000
        public synchronized void add1000(String s) {
            for (int i = 0; i < 1000; i++) {
                try{
                    lock.lock();
                    count++;
                    System.out.println(s + count);
                }finally{
                    lock.unlock();
                }

            }

        }

    }

如上代码便是使用Lock进行加锁,如果你把finally的释放锁代码注释掉,第二个线程就无法执行该,程序一直卡在这,因为线程一一直持有该段代码的锁,线程二一直等待获取锁,便发生了死锁堵塞。

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013592964/article/details/62426441
文章标签: java 线程安全
个人分类: java 编程思想
想对作者说点什么? 我来说一句

java中线程同步方法

2011年11月01日 97KB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭