玛迪网站建设,什么叫营销型网站,网站开发工程师好吗,北京做手机网站的公司在Java中#xff0c;线程同步使用最多的方法是使用synchronized关键字。每个Java对象都隐含有一把锁#xff0c;这里称为Java内置锁(或者对象锁、隐式锁)。使用synchronized(syncObject)调用相当于获取 syncObject 的内置锁#xff0c;所以可以使用内置锁对临界区代码段进行…在Java中线程同步使用最多的方法是使用synchronized关键字。每个Java对象都隐含有一把锁这里称为Java内置锁(或者对象锁、隐式锁)。使用synchronized(syncObject)调用相当于获取 syncObject 的内置锁所以可以使用内置锁对临界区代码段进行排他性保护。
synchronized 同步方法 synchronized 关键字是Java的保留字当使用synchronized关键字修饰一个方法的时候该方法被声明为同步方法具体的例子如下:
//同步方法
public synchronized void selfplus(){amount;
}
关键字synchronized的位置处于同步方法的返回类型之前。回到前面的线程安全小实验现在使用synchronized关键字对临界区代码段进行保护代码如下:
package com.crazymakercircle.plus;// 省略importpublic class SafePlus{private Integer amount -0://临界区代码段使用synchronized进行保护 public synchronized void selfplus () {amount;}
} 再次运行测试用例程序,累加10000次之后最终的结果不再有偏差与预期的结果(10000)是相同的。 在方法声明中设置synchronized同步关键字保证其方法的代码执行流程是排他性的。任何时间只允许一个线程进入同步方法(临界区代码段)如果其他线程需要执行同一个方法那么只能等待和排队。 synchronized 同步块 对于小的临界区我们直接在方法声明中设置synchronized同步关键字可以避免竞态条件的问题。但是对于较大的临界区代码段,为了执行效率,最好将同步方法分为小的临界区代码段。通过下面这个例子来具体讲述:
public class TwoPlus { private int sum1 0; private int sum2 0;//同步方法public synchronized void plus(int vall,int val2){//临界区代码段this.sum1 vall; this.sum2 va12;}
} 在以上代码中临界区代码段包含对两个临界区资源的操作这两个临界区资源分别为sum1和 sum2。使用synchronized对plus(int vall, int val2)进行同步保护之后进入临界区代码段的线程拥有sum1和sum2的操作权并且是全部占用。一旦线程进入当线程在操作sum1而没有操作sum2时也将sum2的操作权白白占用其他的线程由于没有进入临界区只能看着sum2被闲置而不能去执行操作。 所以将synchronized加在方法上如果其保护的临界区代码段包含的临界区资源(要求是相互独立的)多于一个就会造成临界区资源的闲置等待进而会影响临界区代码段的吞吐量、为了提升吞吐量可以将synchronized关键字放在函数体内同步一个代码块。synchronized 同步块的写法是
synchronized (syncObject) //同步块而不是方法
{//临界区代码段的代码块
}在synchronized同步块后边的括号中是一个syncObject对象代表着进入临界区代码段需要获取 syncObject对象的监视锁或者说将syncObject对象监视锁作为临界区代码段的同步锁。由于每一个Java对象都有一把监视锁因此任何Java对象都能作为synchronized的同步锁。 单个线程在synchronized同步块后面的同步锁后才能进入临界区代码段;反过来说当一个线程获得syncObject对象的监视锁后其他线程就只能等待。 使用synchronized同步块对上面的TwoPlus类进行吞吐量的提升改造具体的代码如下:
public class TwoPlus {private int suml0; private int sum2 0;private Integer sumllock - new Integer(1); //同步锁一 private Integer sum2Lock new Integer(2); //同步锁二 public void plus(int val1, int val2){//同步块1synchronized (this.sumlLock){this.sum1 vall;}//同步块2synchronized(this.sum2Lock){this.sum2 val2;}}
} 改造之后对两个独立的临界区资源sum1 和sum2的加法操作可以并发执行了在某一个时刻不同的线程可以对sum1和sum2同时进行加法操作提升了plus(方法的吞吐量。 在TwoPlus代码中由于同步块1和同步块2保护着两个独立的临界区代码段需要两把不同的 syncObject对象锁因此TwoPlus代码新加了sum1Lock和sum2Lock两个新的成员属性。这两个属性没有参与业务处理TwoPlus仅仅利用了sum1Lock 和 sum2Lock的内置锁功能。 synchronized 方法和synchronized同步块有什么区别呢?总体来说synchronized方法是一种粗粒度的并发控制某一时刻只能有一个线程执行该synchronized方法;而synchronized代码块是一种细粒度的并发控制处于synchronized块之外的其他代码是可以被多个线程并发访问的。在一个方法中并不一定所有代码都是临界区代码段可能只有几行代码会涉及线程同步问题。所以synchronized 代码块比synchronized方法更加细粒度地控制了多个线程的同步访问。 synchronized 方法和synchronized代码块有什么联系呢?在Java的内部实现上synchronized方法实际上等同于用一个synchronized代码块这个代码块包含同步方法中的所有语句然后在 synchronized 代码块的括号中传入this关键字使用this对象锁作为进入临界区的同步锁。 例如下面两种实现多线程同步的plus方法版本编译成JVM内部字码后结果是一样的。版本一使用synchronized代码块对方法内部全部代码进行保护具体代码如下:
public void plus () {synchronized(this) { //对方法内部全部代码进行保护amount;}
}版本二使用synchronized方法对方法内部全部代码进行保护具体代码如下:
public synchronized void plus() {amount;
} 综上所述synchronized方法的同步锁实质上使用了this对象锁这样就免去了手工设置同步锁的工作。而使用synchronized代码块需要手工设置同步锁。
静态的同步方法 在Java世界里一切皆对象。Java有两种对象:Object实例对象和Class对象。每个类运行时的类型信息用Class对象表示它包含与类名称、继承关系、字段、方法有关的信息。JVM将一个类加载入自己的方法区内存时会为其创建一个Class对象对于一个类来说其Class对象是唯一的。 Class类没有公共的构造方法Class对象是在类加载的时候由Java虚拟机调用类加载器中的 defineClass 方法自动构造的因此不能显式地声明一个Class对象。 所有的类都是在第一次使用时被动态加载到JVM中的(懒加载)其各个类都是在必需时才加载的。这一点与许多传统语言(如C)都不同JVM为动态加载机制配套了一个判定一个类是否已经被加载的检查动作使得类加载器首先检查这个类的Class对象是否已加载。如果尚未加载,类加载器就会根据类的全限定名查找.class文件,验证后加载到JVM的方法区内存并构造其对应的Class对象。 普通的synchronized实例方法其同步锁是当前对象this的监视锁。如果某个synchronized方法是static(静态)方法而不是普通的对象实例方法其同步锁又是什么呢? 下面展示一个使用synchronized关键字修饰static方法的例子具体如下:
package com.crazymakercircle.plusi
//省略import
public class SafeStaticMethodPlus{ //静态的临界区资源private static Integer amount 0;//使用synchronized关键字修饰 static方法public static synchronized void selfPlus (){amount;}
} 大家都知道静态方法属于Class实例而不是单个Object实例在静态方法内部是不可以访 问Object实例的this引用(也叫指针、句柄)的。所以修饰static方法的synchronized关键字就没有办法获得Object 实例的this对象的监视锁。 实际上使用synchronized关键字修饰static方法时synchronized的同步锁并不是普通Object 对象的监视锁而是类所对应的 Class 对象的监视锁。 为了以示区分这里将Object对象的监视锁叫作对象锁将Class对象的监视锁叫作类锁。当synchronized 关键字修饰static方法时同步锁为类锁当synchronized关键字修饰普通的成员方法(非静态方法)时同步锁为类锁。由于类的对象实例可以有很多但是每个类只有一个Class实例,因此使用类锁作为synchronized的同步锁时会造成同一个JVM内的所有线程只能互斥地进入临界区段。
//对JVM内的所有线程同步
public static synchronized void selfplus (){//临界区代码
} 所以使用synchronized关键字修饰static方法是非常粗粒度的同步机制。 通过synchronized关键字所抢占的同步锁什么时候释放呢?一种场景是synchronized块(代码块或者方法)正确执行完毕监视锁自动释放;另一种场景是程序出现异常,非正常退出 synchronized块,监视锁也会自动释放。所以,使用synchronized块时不必担心监视锁的释放问题。