1. 什么是同步块?
在Java中,同步块是一个被synchronized关键字包裹的代码块。该代码块可以保证在同一时刻只有一个线程可以执行,从而避免多个线程同时访问共享资源而引起的数据不一致的问题。
通常情况下,同步块是用来保证线程安全的。
下面是一个同步块的示例:
public class MyThread extends Thread {
private static int count = 0;
public void run() {
synchronized(this) {
count++;
System.out.println("thread " + this.getName() + " count:" + count);
}
}
}
上面的代码中,MyThread类继承自Thread类,每次执行run方法时,都会在count上加1,并输出执行线程的名称和count的值。由于count是静态变量,可能会被多个线程并发访问,所以我们需要在执行count++这一步时对代码块进行同步。
使用synchronized关键字包裹代码块能够保证在同一时刻只有一个线程可以访问代码块中的代码:
public void run() {
synchronized(this) {
count++;
System.out.println("thread " + this.getName() + " count:" + count);
}
}
这样我们就可以保证在多个线程访问MyThread对象中的count变量时,不会出现数据不一致的情况。
2. 什么时候需要使用同步块?
2.1 同步块用于保护共享资源
当多个线程都要访问同一个共享资源时,为了保证数据的正确性,需要在代码块中对共享资源进行同步。
下面是一个简单的示例:
public class MyThread extends Thread {
private static int count = 0;
public void run() {
synchronized(this) {
count++;
System.out.println("thread " + this.getName() + " count:" + count);
}
}
}
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
}
}
上面的代码中,我们启动了两个线程来访问同一个共享资源count变量。由于count变量可能会被多个线程并发访问,所以我们需要在对count变量进行操作时进行同步。
2.2 同步块用于提高性能
在一些多线程的场景下,使用同步块能够提高程序的性能。
例如,在Java中,StringBuffer和StringBuilder两个类都可以用来进行字符串的拼接,它们之间的区别在于StringBuffer是线程安全的,而StringBuilder则是非线程安全的。
在单线程的场景下,使用StringBuilder会比StringBuffer更快,因为StringBuilder不需要进行线程同步;但在多线程的场景下,使用StringBuilder可能会导致数据不一致的问题,而使用StringBuffer则可以保证数据的正确性。
3. 同步块的限制
3.1 同步块只对引用类型有效
同步块只对引用类型有效,而对于基本类型则无效。
public class MyThread extends Thread {
private static int count = 0;
public void run() {
synchronized(this) {
count++;
System.out.println("thread " + this.getName() + " count:" + count);
}
}
public static void main(String[] args) {
int i = 0;
for (; i < 10; i++) {
new MyThread().start();
}
}
}
上面的代码中,我们在MyThread类中使用同步块来保护count变量。但是,当我们在main方法中创建一个基本类型的变量i,并循环创建MyThread对象时,程序并没有得到同步:
thread Thread-8 count:1
thread Thread-9 count:2
thread Thread-10 count:3
thread Thread-11 count:4
thread Thread-12 count:5
thread Thread-13 count:6
thread Thread-14 count:7
thread Thread-15 count:9
thread Thread-16 count:10
thread Thread-17 count:8
因此,当我们要使用同步块来保护一些数据时,需要保证这些数据是引用类型。
3.2 同步块对性能有一定影响
由于同步块只能在同一时刻让一个线程执行,这样可能会导致程序的性能受到影响。
因此,在使用同步块时,需要对代码进行优化,避免出现线程争用的情况。
4. 总结
在Java中,同步块是用来保证线程安全的重要工具。使用同步块能够保证在同一时刻只有一个线程可以执行代码块中的代码,从而避免多个线程同时访问共享资源而引起的数据不一致的问题。同时,同步块也能提高程序的性能。然而,同步块在使用时需要注意保证被同步的对象是引用类型,并且避免出现线程争用的情况。