sysynchronized关键字可以修饰方法、代码块,但不能修饰构造器、成员变量等。
当sysynchronized关键字同来修饰方法和代码块时,能够保证同一时刻最多只有一个线程执行该段代码或方法。防止当有两个线程并发修改同一个文件时可能会造成异常。
同步代码块语法:
synchronized(obj){
...
//此处代码是同步代码块
}
意思为:当线程要执行以下代码块之前,必须先获得对obj的锁。
当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完代码块以后才能执行该代码块。
然而,另一个当一个线程访问object的一个synchronized(this)代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块,但是其他线程对object中其他所有的synchronized(this)同步代码块的访问将被阻塞。
示例:
public class Thread1 {
public void synchronize() {
synchronized (this) {
System.out.println("I am the synchronized part:");
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("the synchronized part is over");
}
}
public void synchronize2() {
synchronized (this) {
System.out.println("I am the synchronized part 2:");
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("the synchronized part 2 is over");
}
}
public void nonSynchronize() {
System.out.println("non-synchronized part:");
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("the non-synchronized part is over");
}
public static void main(String[] args) {
final Thread1 thread1 = new Thread1();
//启动线程,线程1、2和3分别用了不同的表达式
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
thread1.synchronize();
}
});
Thread t2 = new Thread(() -> thread1.synchronize2());
Thread t3 = new Thread(thread1::nonSynchronize);
t1.start();
t2.start();
t3.start();
}
}输出结果:
从输出结果可以看出:线程0执行的是第一个同步代码块,线程2执行的是非同步代码块,线程0和2同时执行,两者互不干扰。线程1执行的是第二个同步代码块,因为线程0先获得对该对象的锁定,所以线程1即使执行代码块跟线程0不一样,也被阻塞,必须等线程1执行完同步代码块,释放锁,线程1才能继续执行。
同步方法示例
public class Thread1 {
private synchronized void synchronize() {
System.out.println("I am the synchronized method part:");
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("the synchronized method part is over");
}
private synchronized void synchronize2() {
System.out.println("I am the synchronized method part 2:");
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("the synchronized method part 2 is over");
}
private void nonSynchronize() {
System.out.println("non-synchronized method part:");
for (int i=0; i<3; i++) {
System.out.println(Thread.currentThread().getName()+":"+(i+1));
try {
Thread.sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("the non-synchronized method part is over");
}
public static void main(String[] args) {
final Thread1 thread1 = new Thread1();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
thread1.synchronize();
}
});
Thread t2 = new Thread(() -> thread1.synchronize2());
Thread t3 = new Thread(thread1::nonSynchronize);
t1.start();
t2.start();
t3.start();
}
}输出结果跟同步代码块一样:
同步锁
Lock 是控制多个线程对共享资源进行访问的工具。每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
在实现线程安全的控制中,比较常用的是ReentrantLock。
class X {
private final ReentrantLock lock = new ReentrantLock();
//定义需要保证线程安全的方法
public void m() {
//加锁
lock.lock();
try {
//method body
} finally { //使用finally来保证释放锁
lock.unlock();
}
}
}参考资料:java加锁与同步方法
疯狂java讲义(第三版)
Java编程思想(第4版)