java中synchronized同步

synchronized关键字是控制java中线程同步的,能够分为synchronized块和synchronized方法两种状况。弄懂具体功能及它们的异同仍是颇有必要的。java


一、同步方法ide

新建的测试类:测试

//the synchronized functions are in this class
class Executer {
    public synchronized void execute1() {
        for (int i = 0; i < 3; ++i) {
            System.out.println(i);
        }
    }
    
    public synchronized void execute2() {
        for (int i = 0; i < 3; ++i) {
            System.out.println(i);
        }
    }

}

//thread1, it will invoke the function "execute1()" in Executer
class Thread1 extends Thread {
    private Executer executer;

    public Thread1(Executer executer) {
        this.executer = executer;
    }

    @Override
    public void run() {
    	executer.execute1();/
    }

}

//thread2, it will invoke the function "execute2()" in Executer
class Thread2 extends Thread {
    private Executer executer;

    public Thread2(Executer executer) {
        this.executer = executer;
    }

    @Override
    public void run() {
    	executer.execute2();
    }

}

1.一、当两个线程访问同一个对象的同一个同步方法:this

main方法所在类:spa

public class SyncTest {
	public static void main(String[] args) {
        Executer executer = new Executer();//initiate only one instance

        Thread t1 = new Thread1(executer);
        Thread t2 = new Thread1(executer);//t1 and t2 are both instance of Thread1, and they will both invoke the function "execute1()".

        t1.start();
        t2.start();
    }
}

输出结果显然是线程

0
1
2
0
1
2

t1先执行execute1()方法,给其上锁。访问完毕后解锁,再由t2去访问。而若是execute1()不加synchronized方法的话,输出将会是00 11 22,也就是t1, t2两个线程同时访问execute1()方法。如今咱们须要知道这个“锁”是上在哪里的。code

1.二、当两个线程访问同一个对象的不一样同步方法:orm

main方法所在类:对象

public class SyncTest2 {
	public static void main(String[] args) {
        Executer executer = new Executer();//initiate only one instance

        Thread t1 = new Thread1(executer);
        Thread t2 = new Thread2(executer);//t1 will invoke the function "execute1()". And t2 will invoke "execute2()".

        t1.start();
        t2.start();
    }
}

结果执行输出为:
同步

0
1
2
0
1
2

跟上例的结果同样。而咱们这里两个线程访问的明明是两个不一样的方法。这说明当有线程访问同步方法(synchronized)时,会给对象上锁,而不是方法。上锁期间,其余线程不能访问该对象的任何同步方法。直到此线程结束或者抛异常,才会把锁释放。再来进一步研究锁的级别。

1.三、静态方法的同步

将测试类做以下修改(将Executer中的方法改成静态,两个Thread类不变):

 class Executer {
    public synchronized static void execute1() {//static synchronized 
        for (int i = 0; i < 3; ++i) {
            System.out.println(i);//print i.
        }
    }
    
    public synchronized static void execute2() {//static synchronized 
        for (int i = 0; i < 3; ++i) {
            System.out.println(i);//print i.
        }
    }

}

而后在main方法中如是输入:

public class SyncTest2 {
	public static void main(String[] args) {
        Executer executer1 = new Executer();
        Executer executer2 = new Executer();//initiate two instances

        Thread t1 = new Thread1(executer1);//invoke execute1()
        Thread t2 = new Thread2(executer2);//invoke execute2()

        t1.start();
        t2.start();
    }
}

输出结果:

0
1
2
0
1
2

依然同步。这里咱们建立了两个对象实例,按理说不一样线程访问各自对象的同步方法的时候,两个线程间应该互不影响。然而,结果却说明它们之间会有制约关系。问题就出在static关键字。被static修饰的方法为类方法,而非是对象层面的。当方法同时被static synchronized关键字修饰,线程访问此方法时会给这个类上锁,而不单单是对象。期间其余线程没法访问这个类的全部静态同步方法。

2.同步块

其实也能够将Executer类中的方法以下写:

class Executer {
    private Object object = new Object();    
    public void execute1() {        
        synchronized (object) {            
            for (int i = 0; i < 3; ++i) {
                System.out.println(i);
            }
        }
    }
       
    public void execute2() {        
        synchronized (object) {            
            for (int i = 0; i < 3; ++i) {
                System.out.println(i);
            }
        }
    }
    
}

实现的效果跟以前的1.2例同样。原理就是对object对象加上锁,同时只能有一个线程能够访问该方法。

而后如今有这样一个问题:

public class SyncTest extends Thread {
	private static String str = "";
	private String type = "";
	
	private static void method(String s) {
		synchronized (str) {
		        /************
			//str = s;
			************/
			System.out.println(s);
			while(true){
				
			}
		}
	}
	
	public void method1() {
		method("method1");
	}
	
	public static void staticMethod1() {
		method("staticMethod1");
	}
	
	public void run() {
		if(type.equals("static")) {
			staticMethod1();
		}
		else{
			method1();
		}
	}
	
	public SyncTest(String type) {
		this.type = type;
	}
	
	public static void main(String[] args) {
		SyncTest1 test1 = new SyncTest1("nonstatic");
		SyncTest1 test2 = new SyncTest1("static");
		test1.start();
		test2.start();
	}

}

问注释那一行加与不加什么区别?

如下是解开注释的结果:

method1
staticMethod1

而后陷入while无限循环。

如下是注释掉的结果:

method1

也是陷入了while无限循环。


其实产生这个差异的主要缘由是synchronized块里的object改变了。注释那行是修改了此object。若是加上这一行,会致使从新产生一个String对象,此方法快的同步性将被破坏,因而出现了两个方法同时访问这个“同步块”的状况。

顺便说一下,同步块里的参数只能是对象(object),不能是基本类型的数据(如int,byte)。

因为同步块的控制比较细粒度,并且能够传this关键字进去(至关于当前对象的一个synchronized修饰的方法),因此使用上可能会更普遍一点吧。

相关文章
相关标签/搜索