`
king_c
  • 浏览: 222711 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

线程安全 synchronized

    博客分类:
  • java
 
阅读更多

 

package gucas.xiaoxia.demo;

public class Demo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Runnable safe = new ThreadSafe();
		Thread thread1 = new Thread(safe, "thread1");
		Thread thread2 = new Thread(safe, "thread2");
		thread1.start();
		thread2.start();
	}

}

class ThreadSafe implements java.lang.Runnable {

	int num = 1;
	Object o = new Object();

	public void run() {
		for (int i = 0; i < 3; i++) {
			synchronized (this) {
				num = num + 1;
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("num is value +==="
						+ Thread.currentThread().getName() + "---------" + num);
			}
		}
	}

}

输出:

num is value +===thread1---------2

num is value +===thread1---------3

num is value +===thread1---------4

num is value +===thread2---------5

num is value +===thread2---------6

num is value +===thread2---------7

 

在启用同步锁机制以后,需要避免

1无线等待,,线程B等待线程A执行完毕,然后线程A确进入了死循环。

2循环等待:两个线程相互调用,都要求要同步执行,这个时候就先会循环等待,我等你执行,你也在等我执行,这个时候就死锁了。

 

注意:

理论上,每个对象都可以做为锁,但一个对象做为锁时,应该被多个线程共享,这样才显得有意义,在并发环境下,一个没有共享的对象作为锁是没有意义的。假如 有这样的代码:

 

public class ThreadTest{  
  public void test(){  
     Object lock=new Object();  
     synchronized (lock){  
        //do something  
     }  
  }  
}  

 lock变量作为一个锁存在根本没有意义,因为它根本不是共享对象,每个线程进来都会执行Object lock=new Object();每个线程都有自己的lock,根本不存在锁竞争。

 

        每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个被线程被唤醒 (notify)后,才会进入到就绪队列,等待cpu的调度。

 

 

生产者/消费者模式 
        生产者/消费者模式其实是一种很经典的线程同步模型,很多时候,并不是光保证多个线程对某共享资源操作的互斥性就够了,往往多个线程之间都是有协作的。
        假设有这样一种情况,有一个桌子,桌子上面有一个盘子,盘子里只能放一颗鸡蛋,A专门往盘子里放鸡蛋,如果盘子里有鸡蛋,则一直等到盘子里没鸡蛋,B专门 从盘子里拿鸡蛋,如果盘子里没鸡蛋,则等待直到盘子里有鸡蛋。其实盘子就是一个互斥区,每次往盘子放鸡蛋应该都是互斥的,A的等待其实就是主动放弃锁,B 等待时还要提醒A放鸡蛋。
如何让线程主动释放锁
很简单,调用锁的wait()方法就好。wait方法是从Object来的,所以任意对象都有这个方法。看这个代码片段:

 

Object lock=new Object();//声明了一个对象作为锁  
   synchronized (lock) {  
       balance = balance - num;  
       //这里放弃了同步锁,好不容易得到,又放弃了  
       lock.wait();  
}  

 如果一个线程获得了锁lock,进入了同步块,执行lock.wait(),那么这个线程会进入到lock的阻塞队列。如果调用 lock.notify()则会通知阻塞队列的某个线程进入就绪队列。

声明一个盘子,只能放一个鸡蛋

 

import java.util.ArrayList;  
import java.util.List;  
  
public class Plate {  
  
    List<Object> eggs = new ArrayList<Object>();  
  
    public synchronized Object getEgg() {  
        if (eggs.size() == 0) {  
            try {  
                wait();  
            } catch (InterruptedException e) {  
            }  
        }  
  
        Object egg = eggs.get(0);  
        eggs.clear();// 清空盘子  
        notify();// 唤醒阻塞队列的某线程到就绪队列  
        System.out.println("拿到鸡蛋");  
        return egg;  
    }  
  
    public synchronized void putEgg(Object egg) {  
        if (eggs.size() > 0) {  
            try {  
                wait();  
            } catch (InterruptedException e) {  
            }  
        }  
        eggs.add(egg);// 往盘子里放鸡蛋  
        notify();// 唤醒阻塞队列的某线程到就绪队列  
        System.out.println("放入鸡蛋");  
    }  
      
    static class AddThread extends Thread{  
        private Plate plate;  
        private Object egg=new Object();  
        public AddThread(Plate plate){  
            this.plate=plate;  
        }  
          
        public void run(){  
            for(int i=0;i<5;i++){  
                plate.putEgg(egg);  
            }  
        }  
    }  
      
    static class GetThread extends Thread{  
        private Plate plate;  
        public GetThread(Plate plate){  
            this.plate=plate;  
        }  
          
        public void run(){  
            for(int i=0;i<5;i++){  
                plate.getEgg();  
            }  
        }  
    }  
      
    public static void main(String args[]){  
        try {  
            Plate plate=new Plate();  
            Thread add=new Thread(new AddThread(plate));  
            Thread get=new Thread(new GetThread(plate));  
            add.start();  
            get.start();  
            add.join();  
            get.join();  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("测试结束");  
    }  
}  

 

 


 放入鸡蛋  
拿到鸡蛋  
放入鸡蛋  
拿到鸡蛋  
放入鸡蛋  
拿到鸡蛋  
放入鸡蛋  
拿到鸡蛋  
放入鸡蛋  
拿到鸡蛋  
测试结束  

   volatile是java提供的一种同步手段,只不过它是轻量级的同步,为什么这么说,因为volatile只能保证多线程的内存可见性,不能保证多线 程的执行有序性。而最彻底的同步要保证有序性和可见性,例如synchronized。任何被volatile修饰的变量,都不拷贝副本到工作内存,任何 修改都及时写在主存。因此对于Valatile修饰的变量的修改,所有线程马上就能看到,但是volatile不能保证对变量的修改是有序的。什么意思 呢?假如有这样的代码:

public class VolatileTest{  
  public volatile int a;  
  public void add(int count){  
       a=a+count;  
  }  
}  
 

当一个VolatileTest对象被多个线程共享,a的值不一定是正确的,因为a=a+count包含了好几步操作,而此时多个线程的执行是无序的,因 为没有任何机制来保证多个线程的执行有序性和原子性。volatile存在的意义是,任何线程对a的修改,都会马上被其他线程读取到,因为直接操作主存, 没有线程对工作内存和主存的同步。所以,volatile的使用场景是有限的,在有限的一些情形下可以使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
1)对 变量的写操作不依赖于当前值。
2)该变量没有包含在具有其他变量的不变式中
 
volatile只保证了可见性,所以Volatile适合直接赋值的场景,如

 

public class VolatileTest{  
  public volatile int a;  
  public void setA(int a){  
      this.a=a;  
  }  
}  
 


在没有volatile声明时,多线程环境下,a的最终值不一定是正确的,因为this.a=a;涉及到给a赋值和将a同步回主存的步骤,这个顺序可能被 打乱。如果用volatile声明了,读取主存副本到工作内存和同步a到主存的步骤,相当于是一个原子操作。所以简单来说,volatile适合这种场 景:一个变量被多个线程共享,线程直接给这个变量赋值。这是一种很简单的同步场景,这时候使用volatile的开销将会非常小。




  


  
分享到:
评论

相关推荐

    Java线程安全synchronized

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

    synchronized与单例的线程安全

    "synchronized"关键字和单例模式是确保线程安全的两种常见手段。本文将详细探讨这两个概念及其在实现线程安全中的作用。 一、synchronized关键字 synchronized是Java中的一个关键同步机制,用于控制对类或对象的...

    servlet线程安全问题

    Servlet 线程安全问题 Servlet 线程安全问题是指在使用 Servlet 编程时,如果不注意多线程安全性问题,可能会导致难以发现的错误。Servlet/JSP 技术由于其多线程运行而具有很高的执行效率,但这也意味着需要非常...

    关于如何解决HashMap线程安全问题的介绍

    1. 使用Collections.synchronizedMap():Java提供了一个便捷的方法,通过Collections.synchronizedMap()可以将HashMap转换为线程安全的Map。但是需要注意,虽然这个方法可以保证基本的线程安全,但迭代仍然是非线程...

    java线程:两种传统的实现方式.zip

    由于多线程环境下对共享资源的访问可能引发竞态条件,我们使用`synchronized`关键字确保了对`globalSum`的更新是线程安全的。 第二种线程实现方式是实现`Runnable`接口。这种方式更为灵活,因为Java类只能单继承,...

    【Java并发编程】Synchronized关键字实现原理.doc

    Synchronized 关键字是 Java 并发编程中最基本的同步机制,它可以保证线程安全,包括原子性、可见性和有序性。Synchronized 关键字可以修饰方法或代码块,使得在同一时刻只有一个线程可以执行该方法或代码块,从而...

    Java多线程(Synchronized+Volatile+JUC 并发工具原理+线程状态+CAS+线程池)

    JUC(Java.util.concurrent)是 Java 语言中的并发工具包,提供了一些高效的线程安全的工具类,包括 Atomic 变量、Lock 锁、Semaphore 信号量、CountDownLatch 计数器、CyclicBarrier 栅栏、ThreadPool 线程池等。...

    java 多线程synchronized互斥锁demo

    总结来说,Java中的`synchronized`关键字是实现线程同步的关键,它通过互斥锁确保对共享资源的访问是线程安全的。在多线程编程中,合理使用`synchronized`可以有效避免竞态条件,保证程序的正确性和稳定性。对于...

    Synchronized锁在Spring事务管理下线程不安全

    Synchronized锁在Spring事务管理下,导致线程不安全。

    servlet与Struts action线程安全问题分析

    如果没有正确的同步机制(如`synchronized`关键字或`volatile`修饰符),线程间的数据交换可能不一致,从而引发线程安全问题。 解决Servlet的线程安全问题通常有以下几种策略: 1. **避免使用实例变量**:尽可能...

    浅议单例模式之线程安全(转)

    在多线程环境下,线程安全的单例模式尤为重要,因为如果不正确实现,可能会导致多个线程同时创建多个实例,违反了单例模式的基本原则。 在Java中,单例模式通常有以下几种实现方式: 1. 饿汉式(静态常量): ...

    java线程安全测试

    Java线程安全是多线程编程中的一个关键概念,它涉及到多个线程访问共享资源时可能出现的问题。在Java中,线程安全问题通常与并发、内存模型和可见性有关。Java内存模型(JMM)定义了如何在多线程环境下共享数据的...

    Java非线程安全类变线程安全类.pdf

    Java 非线程安全类变线程安全类 Java 中的非线程安全类是指有状态的类,即有属性的类,这些类在多线程...Java 中的非线程安全类可以通过使用 ThreadLocal 或 synchronized 关键字来实现线程安全,避免线程安全问题。

    Java多线程安全集合

    这些是基于非线程安全的集合(如ArrayList、HashMap)通过`Collections.synchronized*`方法转换得到的。例如,`Collections.synchronizedList`和`Collections.synchronizedMap`。同步集合在每个方法上加锁,确保...

    局部变量线程安全测试

    测试可能包括对局部变量的读写操作,以及涉及到同步机制如synchronized关键字,volatile修饰符,或者是使用ThreadLocal等技术来确保线程安全。 在标签中,“局部变量”、“线程”、“安全”、“测试”和“源码”是...

    线程安全测试类

    2. **线程执行顺序**:`synchronized`不仅可以保证线程安全,还可以影响线程的执行顺序。在Java中,线程的调度是不确定的,但`synchronized`可以确保在某个线程进入同步代码块并持有锁后,其他等待的线程会按照FIFO...

    java线程安全总结.doc

    Java线程安全是多线程编程中的一个关键概念,它涉及到在并发环境下如何正确地管理共享资源,确保程序的正确性和一致性。以下是对Java线程安全的深入总结: ### 一、线程安全的定义 线程安全是指当多个线程访问同一...

    Java多线程 - (一) 最简单的线程安全问题

    本篇文章将深入探讨“最简单的线程安全问题”,并结合相关源码和工具来帮助理解。线程安全问题通常涉及到多个线程对共享资源的访问,如果管理不当,可能会导致数据不一致、死锁等问题。 首先,我们需要了解什么是...

Global site tag (gtag.js) - Google Analytics