java 线程安全 synchronized

一、线程安全问题:

并发编程的原则:设计并发编程的目的是为了使程序获得更高的执行效率,但绝不能出现数据一致性(数据准确)问题,如果并发程序连最基本的执行结果准确性都无法保证,那并发编程就没有任何意义。

为什么会出现数据不正确:

  如果一个资源(变量,对象,文件,数据库)可以同时被很多线程使用就会出现数据不一致问题,也就是我们说的线程安全问题。这样的资源被称为共享资源或临界区。

  举个例子:

    一个共享变量m,现在有两个线程同时对它进行累加操作,各执行10000次,那么我么期待的结果是20000,但实际上并不是这样的。看代码:

package com.jalja.base.threadTest;

public class SynchronizedTest implements Runnable{
    private static volatile int m=0;
    public static void main(String[] args) {
        Runnable run=new SynchronizedTest();
        Thread thread1=new Thread(run);
        Thread thread2=new Thread(run);
        thread1.start();
        thread2.start();
        try {
            //join() 使main线程等待这连个线程执行结束后继续执行下面的代码
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m的最终结果:"+m);
    }
    public void run() {
        for(int i=0;i<10000;i++){
            m++;
        }
    }
}

无论运行多少次 m总是小于20000。为什么会出现这样的结果呢?当线程thread1在将m++的结果写入内存之前,线程thread2已经从内存中读取了m的值,并在这个值(过时值)上进行++操作,最后将m=1写入内存中(可能就覆盖了thread1计算的m=1的值,也可能是出现thread1覆盖了thread2的值)。出现这样的结果是必然的。
如何控制多线程操作共享数据引起的数据准确性问题呢?使用“序列化访问临界资源”的方案,即在同一时刻,只能有一个线程访问临界资源,也称作同步互斥访问,也就是保证我们的共享资源每次只能被一个线程使用,一旦该资源被线程使用,其他线程将不得拥有使用权。在Java中,提供了两种方式来实现同步互斥访问:synchronized和Lock。

二、互斥访问之synchronized(同步方法或者同步块)

  互斥锁:顾名思义,就是互斥访问目的的锁。

  举个简单的例子:如果对临界资源加上互斥锁,当一个线程在访问该临界资源时,其他线程便只能等待。

  在Java中,每一个对象都拥有一个锁标记(monitor),也称为监视器,多线程同时访问某个对象时,只有拥有该对象锁的线程才能访问。

  在Java中,可以使用synchronized关键字来标记一个需要同步的方法或者同步代码块,当某个线程调用该对象的synchronized方法或者访问synchronized代码块时,这个线程便获得了该对象的锁,其他线程暂时无法访问这个方法,只有等待这个方法执行完毕或者代码块执行完毕,这个线程才会释放该对象的锁,其他线程才能执行这个方法或者代码块。通过这种方式达到我们上面提到的在同一时刻,只能有一个线程访问临界资源。

  synchronized用法:

  1、同步代码块

synchronized(synObject) {

    }

用法:将synchronized作用于一个给定的对象或类的一个属性,所以每当有线程执行这段代码块,该线程会先请求获取对象synObject的锁,如果该锁已被其他线程占有,那么新的线程只能等待,从而使得其他线程无法同时访问该代码块。

package com.jalja.base.threadTest;

public class SynchronizedTest implements Runnable{
    private static volatile int m=0;
    public static void main(String[] args) {
        Runnable run=new SynchronizedTest();
        Thread thread1=new Thread(run);
        Thread thread2=new Thread(run);
        thread1.start();
        thread2.start();
        try {
            //join() 使main线程等待这连个线程执行结束后继续执行下面的代码
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m的最终结果:"+m);
    }
    public void run() {
        synchronized (this) {
            for(int i=0;i<10000;i++){
                m++;
            }
        }
    }
}

该代码是使用当前对象作为互斥锁,下面我们使用类的一个属性作为互斥锁。

package com.jalja.base.threadTest;

public class SynchronizedTest implements Runnable{
    private static volatile int m=0;
    private Object object=new Object();
    public static void main(String[] args) {
        Runnable run=new SynchronizedTest();
        Thread thread1=new Thread(run);
        Thread thread2=new Thread(run);
        thread1.start();
        thread2.start();
        try {
            //join() 使main线程等待这连个线程执行结束后继续执行下面的代码
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m的最终结果:"+m);
    }
    public void run() {
        synchronized (object) {
            for(int i=0;i<10000;i++){
                m++;
            }
        }
    }
}

1、同步方法

package com.jalja.base.threadTest;

public class SynchronizedTest implements Runnable{
    private static int m=0;
    public static void main(String[] args) {
        Runnable run=new SynchronizedTest();
        Thread thread1=new Thread(run);
        Thread thread2=new Thread(run);
        thread1.start();
        thread2.start();
        try {
            //join() 使main线程等待这连个线程执行结束后继续执行下面的代码
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m的最终结果:"+m);
    }
    public synchronized void run() {
        for(int i=0;i<10000;i++){
            m++;
        }
    }
}

这段代码中,synchronzied作用于一个实例方法,就是说当线程在进入run()方法前,必须获取当前对象实例锁,本例中对象实例锁就是run。在这里提醒大家认真看这三段代码中main函数的实现,在这里我们使用Runnable创建两个线程,并且这两个线程都指向同一个Runnable接口实例,这样才能保证两个线程在工作中,使用同一个对象锁,从而保证线程安全。

时间: 2024-10-15 23:32:50

java 线程安全 synchronized的相关文章

java线程同步-synchronized

什么是"线程同步" ? 所谓线程同步就是若干个线程都需要使用一个 synchronized(同步)修饰的方法,当一个线程使用synchronized方法时,其他线程想使用这个synchronized方法时就必须等待,直到这个线程使用完该 synchronized 方法. 在下面的例子中有两个线程,会计和出纳,他俩共同拥有一个账本,她俩都可以使用saveOrTake(int amount)方法对账本进行访问,会计使用saveOrTake(int mount)方法时,向账本上写入存钱记录:

再谈关于 java 线程,synchronized,wait,notify 的问题

<★cnmm22 动感原创:http://blog.csdn.net/cnmm22/article/details/44758269> 在我的上一篇博客里<http://blog.csdn.net/cnmm22/article/details/44273843>,我谈到了java 的线程,和线程同步的问题,但是我想了一下,可能并没有把有些事情说清楚. 既然没说清楚,所以我再谈谈. 首先,synchronized 只有一个作用,就是保证其中的代码块不被打断. 那说了,有synchro

java线程总结--synchronized关键字,原理以及相关的锁

在多线程编程中,synchronized关键字非常常见,当我们需要进行"同步"操作时,我们很多时候需要该该关键字对代码块或者方法进行锁定.被synchronized锁定的代码块,只能同时有一条线程访问该代码块. 上面是很多人的认识,当然也是我之前对synchronized关键字的浅显认识,其实上面的观点存在一定的偏差.在参考了很多文章以及自己动手测试过相关代码后,我觉得有必要记录下自己对synchronized关键字的一些理解,在这个过程,会简单说说synchronized关键字的具体

【java并发】(2) Java线程同步:synchronized锁住的是代码还是对象

在Java中,synchronized关键字是用来控制线程同步的,就是在多线程的环境下,控制synchronized代码段不被多个线程同时执行.synchronized既可以加在一段代码上,也可以加在方法上. 关键是,不要认为给方法或者代码段加上synchronized就万事大吉,看下面一段代码: class Sync { public synchronized void test() { System.out.println("test开始.."); try { Thread.sle

Java线程(二):线程同步synchronized和volatile

上篇通过一个简单的例子说明了线程安全与不安全,在例子中不安全的情况下输出的结果恰好是逐个递增的(其实是巧合,多运行几次,会产生不同的输出结果),为什么会产生这样的结果呢,因为建立的Count对象是线程共享的,一个线程改变了其成员变量num值,下一个线程正巧读到了修改后的num,所以会递增输出. 要说明线程同步问题首先要说明Java线程的两个特性,可见性和有序性.多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现.拿上篇博文中的例子来说明,在多个线程之间共享了Count类的

JAVA线程安全之synchronized关键字的正确用法

JAVA线程安全关于synchronized关键字的用法,今天才知道原来我一直错了.以为用了synchronized关键字包住了代码就可以线程同步安全了. 测试了下.发现是完全的错了.synchronized必须正确的使用才是真正的线程安全...虽然知道这种写法,一直以为却由于懒而用了错误的方法. 看来基础还没有打好.仍需复习加强!工作中犯这种错误是不可原谅的,要知道使用synchronized关键字的地方都是数据敏感的!汗一把... 先贴代码: [java] view plaincopypri

Java线程同步(synchronized)——卖票问题

卖票问题通常被用来举例说明线程同步问题,在Java中,采用关键字synchronized关键字来解决线程同步的问题. Java任意类型的对象都有一个标志位,该标志位具有0,1两种状态,其开始状态为1,当某个线程执行了synchronized(object)语句后,object对象的标志位变为0状态,直到执行完整个synchronized语句中的代码块后,该对象的标志位又回到1状态. 当一个线程执行到synchronized(object)语句的时候,先检查object对象的标志位,如果为0状态,

JAVA程序设计(18.2)----- 5条线程按照给定规律轮流打印 线程调度 线程池 synchronized wait notify

这次没有使用 有名内部类 将程序控制数据.执行程序都独立出来了 5条线程按照给定规律轮流打印 线程调度 线程池 synchronized wait notify 代码如下: 1.独立出来的内部控制数据类 package com.lovo.homework02; /** * 类:将内部数据独立出来 方便修改和控制 * @author Abe * */ public class PrintThreadContext { /** * 允许执行的线程的key 初始值为3 (第4条线程) */ publi

Java多线程-同步:synchronized 和线程通信:生产者消费者模式

大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同步:synchronized 多个线程同时访问一个对象,可能造成非线程安全,数据可能错误,所谓同步:就是控制多个线程同时访就是控制多线程操作同一个对象时,注意是同一个对象,数据的准确性, 确保数据安全,但是加入同步后因为需要等待,所以效率相对低下. 如:一个苹果,自己一个人去咬怎么都不会出问题,但是