认识java中线程安全问题

来源:互联网 发布:mysql 查看表锁情况 编辑:程序博客网 时间:2024/05/17 08:45

出现场景: 多个线程同时操作一个对象,如果该对象没有线程安全的控制,便会出现线程安全问题。例如:我们有一个类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的释放锁代码注释掉,第二个线程就无法执行该,程序一直卡在这,因为线程一一直持有该段代码的锁,线程二一直等待获取锁,便发生了死锁堵塞。

0 0
原创粉丝点击