您的位置:首页 > 大数据 > 人工智能

synchronized、wait、notify、notifyAll

2016-07-02 11:44 447 查看
因为同一个进程的线程之间可以共享同一个进程空间,所以实现线程的数据共享是很容易的(当然在并发中实现数据结构的一致性则另当别论),也可以使用管道流来进行线程通信(没必要)。此处只说协调控制机制

synchronized、wait、notify、notifyAll

使用Java语言内置关键字synchronized来进行加锁,可以保证任务执行的可见性和原子性,从而实现同步执行。synchronized关键字使用的是对象锁,锁的实现依附于对象,避免了锁的创建,所以接下来就直接默认synchronized就是对对象加锁,便于理解。使用方式上,可以显式对对象加锁synchronized(obj){},也可以隐式对当前对象加锁,就是所谓的同步方法或者synchronized(this){}代码块。这里多说一句,因为synchronized是依附对象的加锁策略,所以在普通方法上加synchronized修饰,加锁范围是当前类的实例,对静态方法上加synchronized修饰,加锁范围为Class类实例,两者属于不同对象,所以不存在竞争问题。

<span style="font-family:FangSong_GB2312;"><span style="font-size:18px;">public class t{
public static void main(String[] args){
final test te=new test();
new Thread(){
public void run(){
synchronized(test.class){
System.out.println("subthread1:"+Thread.currentThread().getId()+"start");
try{
Thread.sleep(3000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("subthread1:"+Thread.currentThread().getId()+"end");
}
}
}.start();

try{
Thread.sleep(100);
}catch(Exception e){
e.printStackTrace();
}
te.plain_meth();
te.staic_meth();
}
}
class test{
public synchronized static void staic_meth(){
System.out.println("main staic_meth:"+Thread.currentThread().getId());
}
public synchronized void plain_meth(){
System.out.println("main plain_meth:"+Thread.currentThread().getId());
}
}</span></span>
执行结果

<span style="font-family:FangSong_GB2312;"><span style="font-size:18px;">subthread1:8start
main plain_meth:1
subthread1:8end
main staic_meth:1</span></span>


调用对象的wait方法,使当前线程进入阻塞状态,直到被notify、notifyAll唤醒。notify、notifyAll方法属于大范围无差别唤醒策略,当前进程空间内的所有调用wait进入阻塞状态的线程都可能响应,没有针对性。

生产者消费者示例

<span style="font-family:FangSong_GB2312;"><span style="font-size:18px;">public class t{
private static final int CAPACITY = 3;
public static void main(String[] args){
Buff buf=new Buff(CAPACITY);
for(int i=0;i<10;i++){
new Producer("Producer "+i,buf).start();
new Consumer("Consumer "+i,buf).start();
}
}
}
class Buff{
private final Object[] obj;
private int index=0;
public Buff(int CAPACITY){
obj=new Object[CAPACITY];
}
public void put(Object o){
obj[index++]=o;
}
public Object take(){
return obj[--index];
}
public boolean isEmpty(){
return index==0;
}
public boolean isFull(){
return obj.length==index;
}
public int getContent(){
return index;
}
}
class Producer extends Thread{
private Buff buf;
public Producer(String name,Buff buf){
super(name);
this.buf=buf;
}
public void run(){
produce();
}
private void produce(){
synchronized(buf){
while(buf.isFull()){
try{
buf.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
buf.put(new Object());
System.out.println(Thread.currentThread().getName()+" put a product");
System.out.println("concurrent buf content: "+buf.getContent());
buf.notify();
}
}
}
class Consumer extends Thread{
private Buff buf;
public Consumer(String name,Buff buf){
super(name);
this.buf=buf;
}
public void run(){
consume();
}
private void consume(){
synchronized(buf){
while(buf.isEmpty()){
try{
buf.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
buf.take();
System.out.println(Thread.currentThread().getName()+" take a product");
System.out.println("concurrent buf content: "+buf.getContent());
buf.notify();
}
}
}
</span></span>


这里说一下,生产者线程判断buf不满使用的检查条件是while(buf.isFull()){},这里使用while循环而不是if判断。因为当某一个消费者线程con调用了notify后,会有某个因为wait而阻塞的生产者线程pro被唤醒,但是此时con仍然占据锁,需要等con代码块执行完毕,释放锁后,pro获得锁开始执行,如果con脑袋秀逗了,notify后,加了一句buf.put(new
Object()),则pro在if语句的wait醒来后,直接向下执行,可能抛出越界异常。

总结

Synchronized、wait、notify、notifyAll可以实现一定程度的线程协调作用,但是在灵活性方面做的不好,引入Lock、Condition可以在一方面弥补这些缺陷,当然API层次的locks包也有不足。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线程 并发 竞争 通信