关闭

认识java中线程安全问题

标签: java线程安全
261人阅读 评论(0) 收藏 举报
分类:

出现场景: 多个线程同时操作一个对象,如果该对象没有线程安全的控制,便会出现线程安全问题。例如:我们有一个类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
查看评论

java 集合中线程安全问题

线程安全类 在集合框架中,有些类是线程安全的,这些都是jdk1.1中的出现的。在jdk1.2之后,就出现许许多多非线程安全的类。 下面是这些线程安全的同步的类: vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前...
  • zgj008
  • zgj008
  • 2013-05-13 17:48
  • 483

Java多线程安全问题及解决方案

Java多线程安全问题及解决方案 一、问题引入 通过最常见的多窗口售票问题引入线程安全的问题。代码如下: 注:这里使用Runnable接口来实现线程,这样做是为了共享代售票这个资源,如果我们使用继承Thread来操作,需要将代售票ticketCount设置为全局的公共变量才能实现效果。 packag...
  • OONullPointerAlex
  • OONullPointerAlex
  • 2016-03-16 22:17
  • 1521

Java中线程安全问题个人理解

线程安全问题是一个比较高深的问题,是很多程序员比较难掌握的一个技术难点,如果一个程序员对线程掌握的很好的话,那么这个程序员的内功修炼的是相当的好。 在这里我主要说一下我对java中如何保证线程安全的一些个人见解,希望对各位有所帮助,那里有不对的地方敬请给位不吝赐教。   线程
  • com360
  • com360
  • 2011-09-18 12:33
  • 1586

Java中线程同步方法

1  wait方法:         该方法属于Object的方法,wait方法的作用是使得当前调用wait方法所在部分(代码块)的线程停止执行,并释放当前获得的调用wait所在的代码块的锁,并在其...
  • guogaoan
  • guogaoan
  • 2014-04-19 19:33
  • 355

JAVA中线程同步方法

JAVA中线程同步方法 1  wait方法:         该方法属于Object的方法,wait方法的作用是使得当前调用wait方法所在部分(代码块)的线程停止执行,并释放当前获得的调用wait所在的代码块的锁...
  • you23hai45
  • you23hai45
  • 2014-03-01 15:46
  • 1479

java六种线程池

六大线程池 本文讲述之前我们提到的Executors类(注意加了s的)中的六个静态方法,分别创建六种不同的线程池对象。之前我们已经提到,ThreadPoolExecutor类的别称就是线程池。它是继承自Executor接口(注意没有s)。继承关系图如下。     ...
  • qq_26963495
  • qq_26963495
  • 2018-01-14 14:13
  • 14

局部变量线程安全的理解

如何理解局部变量线程安全
  • weixin_38178584
  • weixin_38178584
  • 2017-10-11 20:24
  • 78

如何解决线程安全问题

 有2种解决方法。 第一,是采用原子变量,毕竟线程安全问题最根本上是由于全局变量和静态变量引起的,只要保证了对于变量的写操作要么全写要么不写,就可以解决线程安全,定义变量用sig_atomic_t和volatile。 第二,就是实现线程间同步啦,用互斥索,信号量。让线程有序的访问变量就可以...
  • u012437660
  • u012437660
  • 2016-04-18 17:15
  • 1139

Servlet线程安全的问题----总结(应付以后的面试)

首先,对于servlet来说,它的生命周期是:当服务器接收用户的一个请求,就会初始化一个servlet实例对象,之后调用init()方法来进行初始化。这个实例一旦创建,就会驻留在内存里面,以供后续的用户访问的需要。之后会调用service()方法,来进行分发调用,选择是调用doGet()还是doPo...
  • zyjing_love
  • zyjing_love
  • 2012-10-29 15:09
  • 436

Main函数中线程执行顺序

通过有个实例在体现学习的要点: public class Main { public static void main(String[] args) { Thread t = new Thread() { public void run() { print(); } }; t.sta...
  • xu_shichuan
  • xu_shichuan
  • 2017-03-20 11:30
  • 109
    个人资料
    • 访问:74868次
    • 积分:1324
    • 等级:
    • 排名:千里之外
    • 原创:59篇
    • 转载:1篇
    • 译文:0篇
    • 评论:31条