经过前面synchronized的了解,现在我们再来对比一下 synchronized对加锁的对象的用法以及其加锁的对象的不同的结果
一:如果锁的对象是类的实例,那么多线程并发的情况下竞争的是每个类的实例所对应的锁
例1:
class Student extends Thread {
private int age;
public Student(int age) {
this.age = age;
}
public synchronized void printAge(){
System.err.println("age is:"+age);
}
public void printAge(int age) {
synchronized(this){
while (true){
System.out.println(age);
try {
Thread.sleep(500);
} catch (InterruptedException e) { e.printStackTrace();
}
}
}
}
public void run() {
printAge(age);
}
}
class SyncTest {
public static void main(String args[]) {
Student s1 = new Student(1);
s1.start();
s1.printAge();
Student s2 = new Student(3);
s2.start();
s2.printAge();
}
}
运行结果:
age is:1
age is:3
1
3
3
1
3
......
例1中的同步块用了this,同步的是类的实例,所以三个线程非阻塞执行 ------------------------------------------------------------------------------------------------------
二:如果锁的对象是类本身,而不是类所生成的实例,那么线程竞争的是类所对应的锁
例2:
class Student extends Thread {
private int age;
public Student(int age) {
this.age = age;
}
public void printAge(int age) {
synchronized(Student.class){
while (true){
System.out.println(age);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void run() {
printAge(age);
}
}
class SyncTest {
public static void main(String args[]) {
Student s1 = new Student(1);
s1.start();
Student s2 = new Student(3);
s2.start();
}
}
运行结果:
1
1
1
1
1
......
例2中不再对个别的类实例同步而是对类进行同步。对于类Student而言,它只有唯一的类定义.这里有两个线程,两个线程竞争的是类Studnet的锁,所以只有得到这个锁的线程才能执行同步块里的代码
synchronized()同步块括号里的可以是类或一个类实例后的对象,当有一个明确的类或对象作为锁时,就可以这样写,但当没有明确的类或对象作为锁,只是想让一段代码同步时,可以创建一个特殊静态变量或实例变量(但无论是静态变量还是实例变量它都必须是一个对象)来充当锁,如下
class Student extends Thread {
private int age;
private static Object lock=new Object();
//或用 private static byte[] lock = new byte[0];
public Student(int age) {
this.age = age;
}
public void printAge(int age) {
synchronized(lock){
while (true){
System.out.println(age);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void run() {
printAge(age);
}
}
class SyncTest {
public static void main(String args[]) {
Student s1 = new Student(1);
s1.start();
Student s2 = new Student(3);
s2.start();
}
}
这里的效果和例2是一样的,如果把static去掉,就是不用静态变量而换成实例变量,那么效果就跟例1一样了
------------------------------------------------------------------------------------------------------
三:同一个类中,既有对类加了锁,又对类的实例对象加了锁,那么这两种同步方法是否互相阻塞呢?
例3:
class Student extends Thread {
private int age;
public Student(int age) {
this.age = age;
}
public synchronized void printAge(){
System.err.println("age is:"+age);
}
public void printAge(int age) {
synchronized(Student.class){
while (true){
System.out.println(age);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void run() {
printAge(age);
}
}
class SyncTest {
public static void main(String args[]) {
Student s1 = new Student(1);
s1.start();
s1.printAge();
Student s2 = new Student(3);
s2.start();
s2.printAge();
}
}
运行结果:
age is:1
age is:3
1
1
1
1
......
例3中分别对类和类的实例加了锁,这里有三个线程,其中两个线程竞争的是类的锁,而主线程要竞争的是类实例的锁,所以线程s1和s2不会阻塞主线程竞争类实例的锁,但此两个线程竞争类的锁的时候依然会互相阻塞
------------------------------------------------------------------------------------------------------
四:如果一个类中定义了一个synchronized的static函数fun1,也定义了一个synchronized 的instance function(变量函数)fun2,那么这个类的同一实例对象在多线程中分别访问这两个方法时能否构成同步呢?
例4:
public class SyncTest {
public synchronized static void fun1() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
public synchronized void fun2() {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
public static void main(String[] args) {
final SyncTest syncTest = new SyncTest();
Thread t1 = new Thread(new Runnable() {
public void run() {
syncTest.fun1();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
public void run() {
syncTest.fun2();
}
}, "t2");
t1.start();
t2.start();
}
}
运行结果:
t1 : 4
t2 : 4
t1 : 3
t2 : 3
t2 : 2
t1 : 2
t2 : 1
t1 : 1
t2 : 0
t1 : 0
由此可见此情况下不会构成同步,跟例3一样的道理,因为它们的锁都不一样。fun1方法的锁是属于类的(X.Class),而fun2的锁是属于这个类的一个实例对象的