java线程技术5_synchronized
来源:互联网 发布:我是皇进阶数据 编辑:程序博客网 时间:2024/05/02 01:53
1.概述
从《java线程技术4_Volatile 》知道,Volatile只能解决多线程情况下共享变量的一致性问题,而解决不了共享数据操作的原子性问题。如下例
这个程序启动了100个线程,然后每个线程将静态变量n加1.最后使用join方法使这100个线程都运行完后,再输出这个n值。按正常来讲,结果应该是n = 100.可偏偏结果小于100。由于run()并不是原子操作,存在脏数据问题,因此小于100。
即使将public static int n = 0; 改成 public volanote static int n = 0;结果依然是小于100。
synchronized关键字可以保证一个对象实例的一个代码段只被一个线程调用。synchronized可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁。比如多个线程调用对象P1的同步方法,它们之间会形成互斥。但是对对象P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱。
实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
2.synchronized与函数
格式:
public synchronized void run(){ ... }
例如,将上面的代码改成如下
则运行结果必为100。
本程序只实例化一个对象实例myThread,创建了100个线程,调用myThread中的run(),由于加上了synchronized,run()同时只能被一个线程调用,并当前的run执行完后,才能被其他的线程调用。即使当前线程执行到了run方法中的yield方法,也只是暂停了一下。由于其他线程无法执行run方法,因此,最终还是会由当前的线程来继续执行。
注意:如果将代码改为如下
意味着创建了100个线程,每个线程实例化一个对象,这时synchronized就没有任何同步作用。其结果必小于100。
3.synchronized与静态方法
对于静态方法来说,只要加上了synchronized关键字,这个方法就是同步的,无论是使用对象.method(),还是使用类.method()来调用method方法,method都是同步的,并不存在非静态方法的多个实例的问题。
4.synchronized和instance变量
在23种设计模式中的单件(Singleton)模式如果按传统的方法设计,也是线程不安全的,下面的代码是一个线程不安全的单件模式。代码如下
测试是否只创建了一个对象
程序运行结果如下
运行结果可能在不同的运行环境上有所有同,但一般这五行输出不会完全相同。从这个输出结果可以看出,通过getInstance方法得到的对象实例是五个,而不是我们期望的一个。这是因为当一个线程执行了Thread.yield()后,就将CPU资源交给了另外一个线程。由于在线程之间切换时并未执行到创建Singleton对象实例的语句,因此,这几个线程都通过了if判断,所以,就会产生了建立五个对象实例的情况。
解决方法1:为getInstance加上synchronized关键字
public static synchronized Singleton getInstance() { }
解决方法2:在定义Singleton变量时就建立Singleton对象
private static final Singleton sample = new Singleton();
5.注意事项
5.1synchronized不能继承
如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。
当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。
5.2 在定义接口方法时不能使用synchronized关键字
5.3 构造方法不能使用synchronized关键字
5.4 synchronized可以自由放置
正确代码
synchronized不能放在方法返回类型的后面,如下面的代码是错误的:
synchronized关键字只能用来同步方法,不能用来同步类变量,如下面的代码也是错误的:
5.5使用synchronized对整个对象的方法调用都有影响
synchronized锁定的是一个类。
如果在非静态方法method1和method2定义时都使用了synchronized,在 method1未执行完之前,method2是不能执行的。
静态方法和非静态方法的情况类似。但静态和非静态方法不会互相影响。
运行结果如下:
非静态方法method1运行时,method2不能运行
静态方法method3运行时,method4不能运行
6.synchronized与代码块
6.1synchronized与非静态函数代码块
使用同一个SyncBlock类实例时,这三个方法只要有一个正在执行,其他两个方法就会因未获得同步锁而被阻塞。
6.2synchronized与内部类非静态函数代码块
在内类(InnerClass)的方法中使用synchronized块来时,this只表示内类,和外类(OuterClass)没有关系。但内类的非静态方法可以和外类的非静态方法同步。
InnerClass类的method4方法和SyncBlock类的其他三个方法同步,因此,method1、method2、method3和method4四个方法在同一时间只能有一个方法执行。
6.3synchronized与静态函数代码块
在调用静态方法时,对象实例不一定被创建。因此,就不能使用this来同步静态方法,而必须使用Class对象来同步静态方法。
method1和method2方法同时只能有一个方法执行。除了使用class字段得到Class对象外,还可以使用实例的getClass方法来得到Class对象。
我们可以通过Class对象使不同类的静态方法同步,如Test类的静态方法method和StaticSyncBlock类的两个静态方法同步。
Test类的method方法和StaticSyncBlock类的method1、method2方法同步。
注意:
在使用synchronized块同步类方法时,非静态方法可以使用this来同步,而静态方法必须使用Class对象来同步。它们互不影响。当 然,也可以在非静态方法中使用Class对象来同步静态方法。但在静态方法中不能使用this来同步非静态方法。这一点在使用synchronized块 同步类方法时应注意。
从《java线程技术4_Volatile 》知道,Volatile只能解决多线程情况下共享变量的一致性问题,而解决不了共享数据操作的原子性问题。如下例
- /**
- * 共享变量的并发错误问题.
- * @version V1.0 ,2011-4-4
- * @author xiahui
- */
- public class DutyDataThread extends Thread
- {
- public static int n = 0;
- public void run()
- {
- int m = n;
- yield();
- m++;
- n = m;
- }
- public static void main(String[] args) throws Exception
- {
- DutyDataThread myThread = new DutyDataThread ();
- Thread threads[] = new Thread[100];
- for (int i = 0; i < threads.length; i++)
- threads[i] = new Thread(myThread);
- for (int i = 0; i < threads.length; i++)
- threads[i].start();
- for (int i = 0; i < threads.length; i++)
- threads[i].join();
- System.out.println("n = " + DutyDataThread.n);
- }
- }
即使将public static int n = 0; 改成 public volanote static int n = 0;结果依然是小于100。
synchronized关键字可以保证一个对象实例的一个代码段只被一个线程调用。synchronized可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁。比如多个线程调用对象P1的同步方法,它们之间会形成互斥。但是对对象P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱。
实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
2.synchronized与函数
格式:
public synchronized void run(){ ... }
例如,将上面的代码改成如下
- public synchronized void run()
- {
- int m = n;
- yield();
- m++;
- n = m;
- }
本程序只实例化一个对象实例myThread,创建了100个线程,调用myThread中的run(),由于加上了synchronized,run()同时只能被一个线程调用,并当前的run执行完后,才能被其他的线程调用。即使当前线程执行到了run方法中的yield方法,也只是暂停了一下。由于其他线程无法执行run方法,因此,最终还是会由当前的线程来继续执行。
注意:如果将代码改为如下
- //DutyDataThread myThread = new DutyDataThread ();
- Thread threads[] = new Thread[100];
- for (int i = 0; i < threads.length; i++)
- threads[i] = new Thread(new DutyDataThread ());
3.synchronized与静态方法
对于静态方法来说,只要加上了synchronized关键字,这个方法就是同步的,无论是使用对象.method(),还是使用类.method()来调用method方法,method都是同步的,并不存在非静态方法的多个实例的问题。
4.synchronized和instance变量
在23种设计模式中的单件(Singleton)模式如果按传统的方法设计,也是线程不安全的,下面的代码是一个线程不安全的单件模式。代码如下
- /**
- * 单例类.
- * @version V1.0 ,2011-4-4
- * @author xiahui
- */
- public class Singleton {
- private static Singleton sample;
- private Singleton(){
- }
- public static Singleton getInstance(){
- if (sample == null){
- Thread.yield(); // 为了放大Singleton模式的线程不安全性
- sample = new Singleton();
- }
- return sample;
- }
- }
- /**
- * 单例线程类.
- * @version V1.0 ,2011-4-4
- * @author xiahui
- */
- public class SingletonThread extends Thread
- {
- public void run(){
- Singleton singleton = Singleton.getInstance();
- System.out.println(singleton.hashCode());
- }
- public static void main(String[] args)
- {
- Thread threads[] = new Thread[5];
- for (int i = 0; i < threads.length; i++)
- threads[i] = new SingletonThread();
- for (int i = 0; i < threads.length; i++)
- threads[i].start();
- }
- }
- 27744459
- 24355087
- 27744459
- 6927154
- 28737396
解决方法1:为getInstance加上synchronized关键字
public static synchronized Singleton getInstance() { }
解决方法2:在定义Singleton变量时就建立Singleton对象
private static final Singleton sample = new Singleton();
5.注意事项
5.1synchronized不能继承
如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。
当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。
- public class Child extends Parent
- {
- public synchronized void method() { super.method(); }//二者任选其一
- }
5.2 在定义接口方法时不能使用synchronized关键字
5.3 构造方法不能使用synchronized关键字
5.4 synchronized可以自由放置
正确代码
- public synchronized void method();
- synchronized public void method();
- public static synchronized void method();
- public synchronized static void method();
- synchronized public static void method();
- public void synchronized method();
- public static void synchronized method();
- public synchronized int n = 0;
- public static synchronized int n = 0;
5.5使用synchronized对整个对象的方法调用都有影响
synchronized锁定的是一个类。
如果在非静态方法method1和method2定义时都使用了synchronized,在 method1未执行完之前,method2是不能执行的。
静态方法和非静态方法的情况类似。但静态和非静态方法不会互相影响。
- /**
- * 当一个非静态函数,其它非静态函数无法执行.
- * 当一个静态函数,其它静态函数无法执行.
- * @version V1.0 ,2011-4-4
- * @author xiahui
- */
- public class synchronizedThread extends Thread {
- public String methodName;
- public static void method(String s) {
- System.out.println(s);
- while (true)// 死循环
- ;
- }
- public synchronized void method1() {
- method("非静态的method1方法");
- }
- public synchronized void method2() {
- method("非静态的method2方法");
- }
- public static synchronized void method3() {
- method("静态的method3方法");
- }
- public static synchronized void method4() {
- method("静态的method4方法");
- }
- public void run() {
- try {
- getClass().getMethod(methodName).invoke(this);
- } catch (Exception e) {
- }
- }
- public static void main(String[] args) throws Exception {
- synchronizedThread myThread1 = new synchronizedThread();
- for (int i = 1; i <= 4; i++) {
- myThread1.methodName = "method" + String.valueOf(i);
- new Thread(myThread1).start();
- sleep(100);
- }
- }
- }
- 非静态的method1方法
- 静态的method3方法
静态方法method3运行时,method4不能运行
6.synchronized与代码块
6.1synchronized与非静态函数代码块
- public class SyncBlock {
- public void method1()
- {
- synchronized(this) // 相当于对method1方法使用synchronized关键字
- {
- … …
- }
- }
- public void method2()
- {
- synchronized(this) // 相当于对method2方法使用synchronized关键字
- {
- … …
- }
- }
- public synchronized void method3()
- {
-
- }
- }
6.2synchronized与内部类非静态函数代码块
在内类(InnerClass)的方法中使用synchronized块来时,this只表示内类,和外类(OuterClass)没有关系。但内类的非静态方法可以和外类的非静态方法同步。
- public class SyncBlock
- {
- … …
- class InnerClass
- {
- public void method4()
- {
- synchronized(SyncBlock.this)
- {
- … …
- }
- }
- }
- … …
- }
6.3synchronized与静态函数代码块
在调用静态方法时,对象实例不一定被创建。因此,就不能使用this来同步静态方法,而必须使用Class对象来同步静态方法。
- public class StaticSyncBlock{
- public static void method1(){
- synchronized(StaticSyncBlock.class) {
- … …
- }
- }
- public static synchronized void method2() {
- … …
- }
- }
- public class StaticSyncBlock{
- public static StaticSyncBlock instance;
- public StaticSyncBlock(){
- instance = this;
- }
- public static void method1(){
- synchronized(instance.getClass()){
-
- }
- }
- }
我们可以通过Class对象使不同类的静态方法同步,如Test类的静态方法method和StaticSyncBlock类的两个静态方法同步。
- public class Test
- {
- public static void method(){
- synchronized(StaticSyncBlock.class){
- }
- }
- }
注意:
在使用synchronized块同步类方法时,非静态方法可以使用this来同步,而静态方法必须使用Class对象来同步。它们互不影响。当 然,也可以在非静态方法中使用Class对象来同步静态方法。但在静态方法中不能使用this来同步非静态方法。这一点在使用synchronized块 同步类方法时应注意。
- java线程技术5_synchronized
- 3、线程安全_synchronized
- 03_张孝祥_Java多线程_传统线程互斥技术_Synchronized
- java学习初探十八之线程的线程同步_锁机制_synchronized
- [线程]——线程同步与锁定1_synchronized
- [线程]——线程同步与锁定2_synchronized
- java 5线程池技术
- Java线程技术
- java 线程池技术
- JAVA传统线程技术
- java技术之线程
- java线程服用技术
- Java线程技术简介
- Java 传统线程技术
- Java中的线程技术
- Java线程池技术
- java 线程技术详解
- Java线程池技术
- 在线工具
- java线程技术1_入门
- java线程技术2_线程的创建运行终止
- java线程技术3_线程的同步
- java线程技术4_Volatile
- java线程技术5_synchronized
- java线程技术6_线程的挂起和唤醒
- 调查前台各种插件的注意事项
- 反汇编解析#define和const的区别
- poj 2497 高斯消元解同余方程
- onCreateOptionsMenu onMenuItemSelected onOptionsItemSelected onCreateContextMenu onContextItemSelect
- 《编程学习之路》C语言篇-C笔记之C语言概述
- shell 中if 喜欢用的表达式
- Apache POI库简化,仅保留hwpf部分(word文档处理,可用于android)