在Java多线程编程的问题中,“线程安全”和“非线程安全”问题成为整个多线程编程的核心思考问题,其中主要涉及到多个线程对共享变量访问时可能出现的各种错误,通过synchronized的使用机制,以最小程度的实现部分代码的同步执行。Java通过提供synchronized机制,来实现同步。由于synchronized的使用过于的灵活,因此,本文主要对synchronized的一些用法、锁对象和相关问题进行了总结。

synchronized的用法大体上可分为两类,其一,synchronized通过直接修饰类方法,实现同步;其二,synchronized通过构成同步代码块来实现代码块里的程序的同步性。

a. synchronized通过直接修饰类方法


synchronized public void addI(String username) {
		if(username.equals("a")) {
			num = 100;
			System.out.println("a set over");
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else {
			num = 200;
			System.out.println("b set over");
		}
		System.out.println(username + " num="+num);
	}

特点:synchronized通过修饰非静态方法,可使多线程访问该方法时,获得基于对象的锁,从而实现同步的对该方法进行访问

优点:能够实现方法的同步性,从而确保方法的执行不会由于多线程的访问而导致错误的发生

缺点:由于同步访问将导致执行效率的下降,计算机的吞吐率下降,多线程执行时间将更长

注:当synchronized修饰的类方法为静态方法,即由static进行修饰的方法时,线程将获得基于类的锁,即其余未获得锁的线程将无法访问该类的任何部分。区别:当修饰的类方法不是static修饰的方法时,其余线程可访问非同步(synchronized修饰)的方法。

只要对象不变,即使对象的属性被改变,运行的结果还是同步

重要结论:

1. synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁

2. 只有共享资源的读写访问才需要同步化,如果不是共享资源,那么根本就没有同步的必要

b. synchronized同步代码块


public void doLongTimeTask() {
		for(int i = 0; i < 100; i++) {
			System.out.println("nosynchronized threadName="+Thread.currentThread().getName()+" i="+(i+1));
		}
		System.out.println("");
		synchronized(this) {
			for(int i = 0; i < 100; i++) {
				System.out.println("synchronized threadName="+Thread.currentThread().getName()+" i="+(i+1));
			}
		}
	}

该例可看出,相对于synchronized修饰类方法,synchronized同步块能够进一步缩小同步代码的范围,从而保证更多部分的代码是异步执行的,从而很好的保证了多线程下的高资源利用率以及系统的吞吐量

在synchronized同步代码块中,利用synchronized(Object){}该同步形式,可以灵活的对不同的对象Object进行锁限制,通过控制共享变量object来实现不同的同步,由此可能产生死锁问题


public void run() {
		if(username.equals("a")) {
			synchronized(lock1) {
				System.out.println("username = "+username);
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized(lock2) {
					System.out.println("按lock1->lock2代码顺序执行了");
				}
			}
		}
		if(username.equals("b")) {
			synchronized(lock2) {
				System.out.println("username = "+username);
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			synchronized(lock1) {
				System.out.println("按lock2->lock1代码顺序执行了");
			}
		}
	}