一、java中的线程同步与锁
什么是锁呢? 打个比方,就好比现实生活当中的门,每个门都有一把锁,每个同步代码块的入口都有一道“门”,而这道门是加了锁的,要进入这道门去做你想做的事情,必须要获取这个锁的钥匙。java中每个对象有且仅有一个锁,基本数据类型是没有锁的,不能用来同步。
二、java 中的synchronized关键字
synchronized关键字用来标识线程要执行它所作用的代码块所需要的锁。synchronized可以用于方法修饰也可以用于代码块。本质地讲,synchronized作用的是一个代码块。
1,synchronized修饰非静态方法时,该同步方法是在类当前实例的对象(也就是this)上同步,也就是需要获取类当前实例对象的锁才能执行该方法。当一个线程进入类的synchronized方法或在this上同步的代码块时,由于类的所有synchronized方法都是在this上同步的,因此其他线程除了不能该问当前该线程正在访问的方法外,也不能访问该类的其他synchronized方法或在this上同步的代码块。需要特别注意的是,这里所说的不能访问,仅仅指在当前实例的数据范围内。
如:
public class Test1 {
public synchronized void f(){
//方法体
}
}
由于本质上synchronized是同步代码块,所以上面代码等价于:
public void f(){
synchronized(this){
//方法体
}
}
2,synchronized修饰静态方法时,它是在整个类(即类的Class对象)上同步的,也就是说,当一个线程访问了标记为synchronized时,其他线程不能访问该类所有实例的任何synchronized static方法。看下面的代码:
package com.litl.java.test;
import java.util.concurrent.TimeUnit;
public class Test1 {
public synchronized static void f(){
System.out.println("f-start---------------");
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f_end>>>>>>>>>>>>>>>>");
}
public synchronized void g() {
System.out.println("g-start-----------");
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("g_end>>>>>>>>>>>>>");
}
public static void main(String[] args) {
for(int i=0;i<10;i++){
final Test1 t=new Test1();
new Thread(new Runnable() {
@Override
public void run() {
t.f();
t.g();
}
}).start();
}
}
}
打印结果:
f-start---------------
f_end>>>>>>>>>>>>>>>>
f-start---------------
g-start-----------
f_end>>>>>>>>>>>>>>>>
g-start-----------
g_end>>>>>>>>>>>>>
f-start---------------
f_end>>>>>>>>>>>>>>>>
g-start-----------
g_end>>>>>>>>>>>>>
f-start---------------
f_end>>>>>>>>>>>>>>>>
g_end>>>>>>>>>>>>>
f-start---------------
g-start-----------
g_end>>>>>>>>>>>>>
f_end>>>>>>>>>>>>>>>>
g-start-----------
f-start---------------
f_end>>>>>>>>>>>>>>>>
g-start-----------
g_end>>>>>>>>>>>>>
f-start---------------
g_end>>>>>>>>>>>>>
f_end>>>>>>>>>>>>>>>>
g-start-----------
f-start---------------
f_end>>>>>>>>>>>>>>>>
g-start-----------
g_end>>>>>>>>>>>>>
f-start---------------
f_end>>>>>>>>>>>>>>>>
g_end>>>>>>>>>>>>>
f-start---------------
g-start-----------
f_end>>>>>>>>>>>>>>>>
g-start-----------
g_end>>>>>>>>>>>>>
g_end>>>>>>>>>>>>>
可以看到,f()方法是同步执行的,而g()方法是交替执行的。
分析:
由于f()是static方法,加上synchronized后标识为它是在Class上同步的,而g()不是static方法,它是在this上同步的,这两个锁是不一样的,由于main方法中是多个类实例执行f()和g()方法,因此这里f()同步起了作用,而对g()的同步无效。
如果想要g()也同步,可以修改代码使g()在Class上同步:
public void g() {
synchronized(Test1.class){
System.out.println("g-start-----------");
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("g_end>>>>>>>>>>>>>");
}
}
打印结果:
f-start---------------
f_end>>>>>>>>>>>>>>>>
f-start---------------
f_end>>>>>>>>>>>>>>>>
f-start---------------
f_end>>>>>>>>>>>>>>>>
f-start---------------
f_end>>>>>>>>>>>>>>>>
g-start-----------
g_end>>>>>>>>>>>>>
f-start---------------
f_end>>>>>>>>>>>>>>>>
g-start-----------
g_end>>>>>>>>>>>>>
f-start---------------
f_end>>>>>>>>>>>>>>>>
g-start-----------
g_e