线程安全
如果当我们启动了两个线程,一个修改一个对象的数据 ,一个输出同一个对象的数据。
这个时候如果我们不做出同步的时候,是会引发线程安全的问题。
synchronized代码块
class Demo implements Runable{
private Object obj = new Object();public void run(){ while(true){ synchronized(obj){code...} } }
}
这里是什么意思呢?
synchronized(obj)
当运行到这步的时候,判断obj是否被其他的线程拿到,
如果是将不能运行代码里面的东西。如果没有被其他线程拿到 ,那将可以运行代码块里的东西。
那obj在synchronized的参数里算是什么呢?
我们称之为锁。
在这里BZ的理解觉得有点像if语句
if(obj是否被别的线程拿了,如果拿了就进不能进去){}
其实synchronized并不是在一开始才判断 ,后面会解说。
synchronized方法
并不是只有代码块,还可以做函数,
代码体现:
public synchronized void method(){}
其实含义也和代码块也差不多,看看锁是否被拿 拿了 将无法进行执行。
如果不知道的肯定会很疑惑,那么synchronized方法锁是谁,其实是this.
这也是synchronized代码 块和synchronized方法的区别,代码块是可以自己想是谁就谁。
延伸, static 的synchronized 方法。
如果是static 的时候,大家肯定都知道 (如果看过我前面文章的话),static方法是在创建类之前就有的 ,如果在加载static 方法到内存的时候,没有这个类 ,将会报错。所以这里锁肯定不会是this,那么除了this 还有谁呢? 大家都知道静态是在类里的唯一对象。那个描述的文件,类.class(bz也不知道怎么解释哈哈),所以它的锁就是当前对象的类文件。
synchronized弊端
一.死锁
有两种情况下会导致死锁的发生
1..当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了
其他的同步。这时容易引发一种现象,死锁。
代码体现:
boolean trueorfalse = true;
Object obj1 = new Object();
Object obj2 = new Object();
if(trueorfalse){while(true){ synchronized(obj1){synchronized(obj2){code..}} }
else{
while(true){ synchronized(obj2){synchronized(obj1){code..}} }
}
如果这样会进行死锁
2..另外一种死锁 就是 所有线程都被wait 没有另一个线程唤醒这些线程
二.不能控制notify对指定线程的唤醒
锁与监视器
当我们在synchronized 里面 调用 wait 方法时 , 该线程就会等待下来,那么当我们要唤醒他的时候,那我们该如何找到被等待下来的线程?
其实这个线程就在该锁的线程池上,那又谁来操作这一系列的动作呢 ,
就是监视器。那么监视器和锁是几对几的呢?这是我们值得思考的问题。
接下来我们来看一下的代码:
class UtilDemo{
boolean flag = false;public synchronized void run1(){ if(flag) try{this.wait();}catch(InterruptException e ){} flag=true; this.notify(); } public synchronized void run2(){ if(!flag) try{this.wait();}catch(InterruptException e ){} flag=false; this.notify(); }
}
class runable1 implements Runable{private UtilDemo u ; public runable1(UtilDemo u){ this.u = u; } public void run(){ while(true){ u.run1(); } }
}
class runable2 implements Runable{private UtilDemo u ; public runable2(UtilDemo u){ this.u = u; } public void run(){ u.run2(); }
}
public class MainDemo{public static void main(String[] args){ UtilDemo u = new UtilDemo(); Runable run1 = new Runable1(u); Runable run2 = new Runable2(u); Thread t1 = new Thread(run1); Thread t2 = new Thread(run1); Thread t3 = new Thread(run2); Thread t4 = new Thread(run2); t1.start(); t2.start(); t3.start(); t4.start(); }
}
当我们启动多个线程的时候后,会出现多个线程等待的情况下,只有一个线程没被等待,当我们这时候调用notify() ,这时候就纠结了到底是唤醒哪一个呢?监视器还提供了一个方法 notifyAll(),但是这是不利于我们的程序的。
Locks
在java .util .concurrent.locks 中有针对的解决方案。
Lock接口,比同步更厉害,有更多的操作。
在JDK1.5中 ,同步改成了Lock
那么原来的锁上的监视器方法(wait,notify,notifyAll)
也应该替换成新锁的监视器方法。
jdk1.5 中将这些原有的监视器方法封装到一个Condition对象中,想要获得监视器方法,需要先获取Condition对象。
Condition 对象的出现其实就是替代了Object中的监视器方法。
所以我们可以对以上代码进行改编:
class UtilDemo{
boolean flag = false;
Lock lock = new Lock();
Condition con1 = lock.newCondition();
Condition con2 = lcok.newCondition();public void run1(){ try{ lock.lock(); if(flag) try{con1.wait();}catch(InterruptException e ){} flag=true; con2.notify(); }finally{ lock.unlock(); } } public void run2(){ try{ lock.lock(); if(!flag) try{con2.wait();}catch(InterruptException e ){} flag=false; con1.notify(); }finally{ lock.unlock(); } }
}
class runable1 implements Runable{private UtilDemo u ; public runable1(UtilDemo u){ this.u = u; } public void run(){ while(true){ u.run1(); } }
}
class runable2 implements Runable{private UtilDemo u ; public runable2(UtilDemo u){ this.u = u; } public void run(){ u.run2(); }
}
public class MainDemo{public static void main(String[] args){ UtilDemo u = new UtilDemo(); Runable run1 = new Runable1(u); Runable run2 = new Runable2(u); Thread t1 = new Thread(run1); Thread t2 = new Thread(run1); Thread t3 = new Thread(run2); Thread t4 = new Thread(run2); t1.start(); t2.start(); t3.start(); t4.start(); }
}
这样我们将就可以对指定的线程唤醒了。
synchronized和Locks区别在哪?
Condition对象里面封装了Object 中监视器的方法。
await():会让线程处于等待状态,其实就是将线程临时存储到线程池中。
signal():会唤醒线程池中任意一个等待的线程。
signalAll():会唤醒线程池中所有的等待线程。
那么这样两种方式没什么区别 不过就是重新封装了一次
区别在于
第一种方法锁是和监视器绑在一起 一个锁只能有一个监视器
第二种方法锁是和监视器分开 Lock 、Condition 一个锁能同时有两个监视器