**
1. 什么是线程同步?
**
当多个线程共同访问同一资源时,会引发问题,所以需要线程同步来保证对资源的访问有序进行
例如:
class Toilet implements Runnable{
//记录可用的厕所数目
private int toilet = 1;
public void run() {
if(toilet>0) {
toilet--;
System.out.println(Thread.currentThread().getName()+"在厕所,别人不要进来");
}
toilet++;
System.out.println(Thread.currentThread().getName()+"从厕所出来了,别人可以进来");
}
}
public class Hello{
public static void main(String []args) {
Toilet t = new Toilet();
for(int i =0;i<10;i++) {
new Thread(t,i+"").start();
}
}
}
输出:
0在厕所,别人不要进来
0从厕所出来了,别人可以进来
1在厕所,别人不要进来
1从厕所出来了,别人可以进来
3在厕所,别人不要进来
3从厕所出来了,别人可以进来
2在厕所,别人不要进来
2从厕所出来了,别人可以进来
4在厕所,别人不要进来
4从厕所出来了,别人可以进来
5在厕所,别人不要进来
5从厕所出来了,别人可以进来
6在厕所,别人不要进来 //6还没出来,7就进去了,这显然不合理
7在厕所,别人不要进来
7从厕所出来了,别人可以进来
6从厕所出来了,别人可以进来
9在厕所,别人不要进来
8在厕所,别人不要进来
8从厕所出来了,别人可以进来
9从厕所出来了,别人可以进来
以上显然不合理,线程对资源的使用很随意,当多个线程对同一资源进行修改的时候,会照成混乱。
2. JAVA中线程同步的方式
1. 采用synchronized关键字定义代码块
synchronized (obj){
…
代码
}
其中obj是同步监视器,一般将可能被多个线程使用的共享资源充当同步监视器,任何时刻只可能有一个线程能够访问同步监视器,当代码执行完之后才会释放同步监视器。
以下情况会释放同步监视器
- 同步方法、同步代码块执行完毕
- 同步方法、同步代码中遇到return、break
- 出现Error、Exception导致异常结束
- 当前程序执行了同步监视器的wait方法
以下情况不会释放同步监视器
- 当前程序调用Thread.sleep、Thread.yield方法,暂停当前方法的执行
- 其他线程调用当前线程的suspend方法将当前线程挂起
class Toilet implements Runnable{
//记录可用的厕所数目
private int toilet = 1;
public void run() {
synchronized(this) { //当前的对象
if(toilet>0) {
toilet--;
System.out.println(Thread.currentThread().getName()+"在厕所,别人不要进来");
}
toilet++;
System.out.println(Thread.currentThread().getName()+"从厕所出来了,别人可以进来");
}
}
}
public class Hello{
public static void main(String []args) {
Toilet t = new Toilet();
for(int i =0;i<10;i++) {
new Thread(t,i+"").start();
}
}
}
输出:
0在厕所,别人不要进来
0从厕所出来了,别人可以进来
1在厕所,别人不要进来
1从厕所出来了,别人可以进来
2在厕所,别人不要进来
2从厕所出来了,别人可以进来
3在厕所,别人不要进来
3从厕所出来了,别人可以进来
4在厕所,别人不要进来
4从厕所出来了,别人可以进来
5在厕所,别人不要进来
5从厕所出来了,别人可以进来
6在厕所,别人不要进来
6从厕所出来了,别人可以进来
8在厕所,别人不要进来
8从厕所出来了,别人可以进来
7在厕所,别人不要进来
7从厕所出来了,别人可以进来
9在厕所,别人不要进来
9从厕所出来了,别人可以进来
一切正常了
2.采用synchronized关键字定义方法
class Toilet implements Runnable{
//记录可用的厕所数目
private int toilet = 1;
public synchronized void run() { //synchronized应该放在void之前
if(toilet>0) {
toilet--;
System.out.println(Thread.currentThread().getName()+"在厕所,别人不要进来");
}
toilet++;
System.out.println(Thread.currentThread().getName()+"从厕所出来了,别人可以进来");
}
}
public class Hello{
public static void main(String []args) {
Toilet t = new Toilet();
for(int i =0;i<10;i++) {
new Thread(t,i+"").start();
}
}
}
输出:
0在厕所,别人不要进来
0从厕所出来了,别人可以进来
3在厕所,别人不要进来
3从厕所出来了,别人可以进来
1在厕所,别人不要进来
1从厕所出来了,别人可以进来
2在厕所,别人不要进来
2从厕所出来了,别人可以进来
4在厕所,别人不要进来
4从厕所出来了,别人可以进来
5在厕所,别人不要进来
5从厕所出来了,别人可以进来
8在厕所,别人不要进来
8从厕所出来了,别人可以进来
6在厕所,别人不要进来
6从厕所出来了,别人可以进来
9在厕所,别人不要进来
9从厕所出来了,别人可以进来
7在厕所,别人不要进来
7从厕所出来了,别人可以进来
一切也正常。
3.采用同步锁Lock来控制线程同步
Lock是JAVA.util.concurrent.locks里的接口
一般使用ReentrantLock(可重入锁)类。
例如:
class Toilet implements Runnable{
private final ReentrantLock lock = new ReentrantLock();
public void run() {
lock.lock(); //上锁
try {
System.out.println(Thread.currentThread().getName()+"在厕所,别人不要进来");
System.out.println(Thread.currentThread().getName()+"出来了,别人可以进去");
} finally {
lock.unlock(); //释放锁
}
}
}
public class Hello{
public static void main(String []args) {
Toilet t = new Toilet();
for(int i =0;i<10;i++) {
new Thread(t,i+"").start();
}
}
}
使用线程同步会使程序运行速度变慢,所以只有需要同步的地方才去使用同步