Synchronized
( /'sɪŋkrənaɪzd/):(sin k rui na z d):同步的
synchronized是Java中的关键字,可修饰实例方法,静态方法,代码块。
代码案例:
synchronized可以修饰实例方法,静态方法,代码块。
修饰实例方法:对当前实例加锁,进入同步代码前要获得当前实例的锁
修饰静态方法:对当前类对象加锁,进入同步代码前要获得当前类对象的锁
修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
具体区别详见代码示例:
0.未加锁时状态
public class TestSyn implements Runnable {
static int count = 0;
public void increase() {
for (int i = 0; i < 10000; i++) {
count++;
}
}
@Override
public void run() {
increase();
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new TestSyn());
Thread t2 = new Thread(new TestSyn());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
}
}
该方法启动两线程对同一个变量进行操作,期望结果为20000
,但本地运行结果为:17168
(每次运行结果不同,但都是小于20000),是由于两个线程同时对该变量进行操作所导致的。加锁即可解决同时进行操作的问题。
1.实例方法
public class TestSyn implements Runnable {
static int count = 0;
public synchronized void increase() {
for (int i = 0; i < 10000; i++) {
count++;
}
}
@Override
public void run() {
increase();
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new TestSyn());
Thread t2 = new Thread(new TestSyn());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
}
}
此处已加锁,但本地运行结果为:15317(每次运行结果不同,但都是小于20000),说明没有锁住。分析代码,syn修饰实例方法,锁住的是对象,但是启动线程时是新new的对象,这也就意味着存在着两个不同的实例对象锁,导致两个线程都拿到了各自的锁,同时进入了increase方法,无法保证线程安全。
对demo进行改进如下(使用同一个对象启动不同线程):
public class TestSyn implements Runnable {
static int count = 0;
public synchronized void increase() {
for (int i = 0; i < 10000; i++) {
count++;
}
}
@Override
public void run() {
increase();
}
public static void main(String[] args) throws InterruptedException {
TestSyn ts = new TestSyn();
Thread t1 = new Thread(ts);
Thread t2 = new Thread(ts);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
}
}
输出结果与期望一致,为20000
,说明锁已生效。
2. 静态方法
public class TestSyn implements Runnable {
public static final Object lockHelper = new Object();
static int count = 0;
public synchronized static void increase() {
for (int i = 0; i < 10000; i++) {
count++;
}
}
@Override
public void run() {
increase();
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new TestSyn());
Thread t2 = new Thread(new TestSyn());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
}
}
运行结果为20000
,证明锁生效。此处虽然与1的失败demo大体一样,但是成功,是因为syn修饰的是静态方法,锁的是类对象,虽然有两个不同实体,但是是同一个类对象,保证线程安全。
3. 修饰一个代码块
public class TestSyn implements Runnable {
public static final Object lockHelper = new Object();
static int count = 0;
public void increase() {
System.out.println("其他无需保证线程安全的耗时操作");
synchronized (lockHelper) {
for (int i = 0; i < 10000; i++) {
count++;
}
}
}
@Override
public void run() {
increase();
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new TestSyn());
Thread t2 = new Thread(new TestSyn());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
}
}
运行结果为20000
,证明锁生效。同2,此处锁住的是类的静态变量,所有此类的对象共用一个静态变量,所以能成功锁住,保证线程安全。
与2相比的优点在于,可以将锁粒度降低,只锁需要保证线程安全的代码,其他无需保证线程安全且耗时的操作可以同步进行,增加代码执行效率。