synchronized常用来保证线程安全,作为java中比较复杂的一个关键字,有必要专门进行一次总结。
大体上来说,用该关键字修饰方法或代码块时,能保证同一时刻,只能有一个线程访问该属性或代码块。
关于synchronized(this)同步代码块,有如下两点需要注意:
- 对于同一个对象的所有synchronized(this)同步代码块,只能有某一个该代码块被某一个线程执行
- 某个线程访问synchronized(this)代码块时,其他线程仍可访问非synchronized(this)代码块
另外需要强调的是,以上规则只对同一个对象实例化的多个线程有效,即多线程需要由同一个对象实例化。
public class Test{
public static void main(String[] arcgs) throws Exception {
//用来实例化的对象
ThreadTest threadTest = new ThreadTest();
Thread thread1 = new Thread(threadTest);
Thread thread2 = new Thread(threadTest);
thread1.start();
thread2.start();
}
}
class ThreadTest implements Runnable{
private int count = 0;
public void run(){
synchronized(this){
for(int i = 0;i<4;i++){
count++;
System.out.println(Thread.currentThread().getName()+" "+count);
}
}
}
}
/**
* Thread-0 1
* Thread-0 2
* Thread-0 3
* Thread-0 4
* Thread-1 5
* Thread-1 6
* Thread-1 7
* Thread-1 8
* /
当然对于非synchronized(this)代码块,其他线程仍可以执行。
public class Test{
public static void main(String[] arcgs) throws Exception {
//用来实例化的对象
ThreadTest threadTest = new ThreadTest();
Thread thread1 = new Thread(threadTest);
Thread thread2 = new Thread(threadTest);
thread1.start();
thread2.start();
}
}
class ThreadTest implements Runnable{
private int count = 0;
public void run(){
for(int i = 0;i<3;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
synchronized(this){
for(int i = 0;i<3;i++){
count++;
System.out.println(Thread.currentThread().getName()+" synchronized "+" "+count);
}
}
}
}
/**
Thread-0 0
Thread-1 0
Thread-0 1
Thread-1 1
Thread-0 2
Thread-1 2
Thread-0 synchronized 1
Thread-0 synchronized 2
Thread-0 synchronized 3
Thread-1 synchronized 4
Thread-1 synchronized 5
Thread-1 synchronized 6
* /
为了更好地理解synchronized关键字修饰方法的意义,看下面的例子:
ThreadTest threadTest = new ThreadTest();
Thread thread1 = new Thread(new Runnable(){
public void run(){
threadTest.test0();
}
});
Thread thread2 = new Thread(new Runnable(){
public void run(){
threadTest.test1();
}
});
thread1.start();
thread2.start();
class ThreadTest {
private int count = 0;
public synchronized void test0(){
for(int i = 0;i<3;i++){
count++;
System.out.println(Thread.currentThread().getName()+" synchronized0 "+" "+count);
}
}
public synchronized void test1(){
for(int i = 0;i<3;i++){
count++;
System.out.println(Thread.currentThread().getName()+" synchronized1 "+" "+count);
}
}
}
/**
Thread-0 synchronized0 1
Thread-0 synchronized0 2
Thread-0 synchronized0 3
Thread-1 synchronized1 4
Thread-1 synchronized1 5
Thread-1 synchronized1 6
* /
虽然没有实现Runnable接口,但由于是在建立的内部类中运行,因此仍然实现了单线程的访问。
注意理解synchronized(this)的意思。
下面的代码等做到单线程执行吗?
ThreadTest threadTest = new ThreadTest();
Thread thread1 = new Thread(new Runnable(){
public void run(){
synchronized(this){
threadTest.test0();
}
}
});
Thread thread2 = new Thread(new Runnable(){
public void run(){
synchronized(this){
threadTest.test1();
}
}
});
thread1.start();
thread2.start();
class ThreadTest {
private int count = 0;
public void test0(){
for(int i = 0;i<3;i++){
count++;
System.out.println(Thread.currentThread().getName()+" synchronized0 "+" "+count);
}
}
public void test1(){
for(int i = 0;i<3;i++){
count++;
System.out.println(Thread.currentThread().getName()+" synchronized1 "+" "+count);
}
}
}
/** 一种可能的输出结果
Thread-1 synchronized1 2
Thread-0 synchronized0 2
Thread-1 synchronized1 3
Thread-0 synchronized0 4
Thread-1 synchronized1 5
Thread-0 synchronized0 6
* /
原因在于,synchronized(this)的目的是获取当前对象的锁,但代码中两次new Thread()中的参数是新建的两个内部类,无法起到互斥的作用。只需要将synchronized(this)改为synchronized(threadTest),就可以得到单线程运行的结果,如下:
/** 一种可能的输出结果
Thread-0 synchronized0 1
Thread-0 synchronized0 2
Thread-0 synchronized0 3
Thread-1 synchronized1 4
Thread-1 synchronized1 5
Thread-1 synchronized1 6
* /
在 Java 中,不光是类对象,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。