前言
在Java编程中有一个每个程序猿想躲躲不开遇到了又十分头疼的问题,即多线程的线程安全问题。这块知识应该可以算是Java中比较麻烦的一块知识之一了,今天就来谈一谈Java中如何解决线程安全问题,以及各种锁的区别。
线程安全问题的主要诱因
1.存在共享数据(临界资源)
2.存在多条线程共同操作这些数据
解决问题的根本方法:同一时刻有且只有一个线程在操作共享数据,其它线程必须等到该线程处理完数据后再对共享数据进行操作。
Synchronized
- 互斥锁的特性
互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程的协调机制,这样在同一时间只有一个线程对需要同步的代码块(复合操作)进行访问。互斥性也称为操作的原子性。
可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作,从而引起不一致。
对于Java来说,Synchronized关键字可以满足上述两种特性。
注:Synchronized锁的不是代码,而是对象。
Synchronized关于获取锁的分类
对象锁
获取对象锁的两种用法:
1.同步代码块(synchronized(this),synchronized(类实例对象)),锁是括号中的对象。
2.同步非静态方法(synchronized method),锁是当前对象的实例。
类锁
获取类锁的两种用法:
1.同步代码块(synchronized(对象.getClass()),synchronized(类名.class)),锁是括号内对象的类对象(class对象)。
2.同步静态方法(synchronized static method),锁是当前对象的class对象。
对象锁和类锁测试
代码
ThreadDemo.java:
public class ThreadDemo implements Runnable{
public void asynMethod() {
System.out.println(Thread.currentThread().getName()+" 开始运行(异步方法)");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 结束运行(异步方法)");
}
public synchronized void syncMethod() {
System.out.println(Thread.currentThread().getName()+" 开始运行(同步方法)");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 结束运行(同步方法)");
}
public void syncBlock() {
synchronized(this) {
System.out.println(Thread.currentThread().getName()+" 开始运行(同步代码块)");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 结束运行(同步代码块)");
}
}
public void syncClass() {
synchronized (this.getClass()) {
System.out.println(Thread.currentThread().getName()+" 开始运行(以class文件为锁对象的同步代码块)");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 结束运行(以class文件为锁对象的同步代码块)");
}
}
public static synchronized void syncStaticMethod() {
System.out.println(Thread.currentThread().getName()+" 开始运行(静态同步方法(以class文件为锁))");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 结束运行(静态同步方法(以class文件为锁))");
}
@Override
public void run() {
// TODO Auto-generated method stub
if(Thread.currentThread().getName().startsWith("ASYN")) {
asynMethod();
}else if(Thread.currentThread().getName().startsWith("SYNC_METHOD")) {
syncMethod();
}else if(Thread.currentThread().getName().startsWith("SYNC_BLOCK")) {
syncBlock();
}else if(Thread.currentThread().getName().startsWith("SYNC_CLASS")) {
syncClass();
}else if(Thread.currentThread().getName().startsWith("SYNC_STATIC")) {
syncMethod();
}
}
}
ThreadTest.java:
public class ThreadTest {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
Thread thread1 = new Thread(threadDemo,"ASYN_Thread1");
Thread thread2 = new Thread(threadDemo,"ASYN_Thread2");
Thread thread3 = new Thread(threadDemo,"SYNC_METHOD_Thread1");
Thread thread4 = new Thread(threadDemo,"SYNC_METHOD_Thread2");
Thread thread5 = new Thread(threadDemo