一、前言
使用synchronized声明的方法在 某些情况下是有弊端的,比如A线程调用同步的方法执行一个长时间的任务,那么B线程就必须等待比较长的时间才能执行,这种情况可以使用synchronized代码块去优化代码执行时间,也就是通常所说的减少锁的粒度。
/**
* 使用synchronized代码块减小锁的粒度,提高性能
* @author alienware
*
*/
public class Optimize {
public void doLongTimeTask(){
try {
System.out.println("当前线程开始:" + Thread.currentThread().getName() +
", 正在执行一个较长时间的业务操作,其内容不需要同步");
Thread.sleep(2000);
synchronized(this){
System.out.println("当前线程:" + Thread.currentThread().getName() +
", 执行同步代码块,对其同步变量进行操作");
Thread.sleep(1000);
}
System.out.println("当前线程结束:" + Thread.currentThread().getName() +
", 执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
final Optimize otz = new Optimize();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
otz.doLongTimeTask();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
otz.doLongTimeTask();
}
},"t2");
t1.start();
t2.start();
}
}
当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一段时间内只能有一个线程被执行,另一个线程必须等待当前线程执行完这个同步代码块以后才能执行该代码块。当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块。
二、将任意对象作为对象监视器
多个线程调用同一个对象中的不同名称的synchronized同步方法或synchronized(this)同步代码块时,调用的效果就是按顺序执行,也就是同步的,阻塞的。
这说明synchronized同步方法或synchronized(this) 同步代码块分别有两种作用。
(1)synchronized同步方法
①对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态。
②同一时间只有一个线程可以执行synchronized同步方法中的代码
(2)synchronized(this)同步代码块
①对其他synchronized同步方法或synchronized(this)同步代码块呈阻塞状态。
②同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码。
在前面的学习中,使用synchronized(this)格式来同步代码块,其实Java还支持对“任意对象”作为“对象监视器”来实现同步的功能。这个“任意对象”大多数是实例变量及方法的参数,使用格式为synchronized(非this对象)。
service类:
//Java还支持对任意对象作为对象监视器来实现同步功能,非this对象
public class service {
private String usernameParam;
private String passwordParam;
private String anyString= new String();
public void setUsernamePassword(String username,String password){
try {
synchronized (anyString) {
System.out.println("线程名称为: "+Thread.currentThread().getName()+"在 "+System.currentTimeMillis()+" 进入同步代码块");
usernameParam=username;
Thread.sleep(3000);
passwordParam=password;
System.out.println("线程名称为: "+Thread.currentThread().getName()+"在 "+System.currentTimeMillis()+" 离开同步代码块");
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
ThreadA:
package synchronizedCodeblock.mytask;
public class ThreadA extends Thread {
private service service;
public ThreadA(service service){
super();
this.service=service;
}
@Override
public void run(){
service.setUsernamePassword("a", "aa");
}
}
ThreadB
package synchronizedCodeblock.mytask;
public class ThreadB extends Thread {
private service service;
public ThreadB(service service){
super();
this.service=service;
}
@Override
public void run(){
service.setUsernamePassword("b", "bb");
}
}
RunTest
package synchronizedCodeblock.mytask;
public class RunTest {
public static void main(String[] args) {
service service =new service();
ThreadA a= new ThreadA(service);
a.setName("A");
a.start();
ThreadB b=new ThreadB(service);
b.setName("B");
b.start();
}
}
最终实现了同步效果,输出结果如下:
锁非this对象具有一定的优点:如果一个类中有很多个synchronized方法,这时虽然能实现同步,但会受到阻塞,所以影响消息;但如果使用同步代码块锁非this对象,则synchronized(非this)代码块的程序与同步方法是异步的。不与其他锁this同步方法争抢this锁,则可以大大提高运行效率。
将service代码更改如下:
package synchronizedCodeblock.mytask;
//Java还支持对任意对象作为对象监视器来实现同步功能,非this对象
public class service {
private String usernameParam;
private String passwordParam;
//private String anyString= new String();
public void setUsernamePassword(String username,String password){
try {
String anyString= new String();
synchronized (anyString) {
System.out.println("线程名称为: "+Thread.currentThread().getName()+"在 "+System.currentTimeMillis()+" 进入同步代码块");
usernameParam=username;
Thread.sleep(3000);
passwordParam=password;
System.out.println("线程名称为: "+Thread.currentThread().getName()+"在 "+System.currentTimeMillis()+" 离开同步代码块");
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
最终输出结果:不是同步的而是异步的(因为不是同一个锁,new String()作为对象监视器)如下图所示
使用synchronized(非this对象x)同步代码块 进行同步操作时,对象监视器必须是同一个对象。如果不是同一个对象监视器,运行的结果就是异步调用了,就会交叉运行。