线程同步

来源:互联网 发布:刷排名软件 编辑:程序博客网 时间:2024/06/14 17:29

synchronized是属于对象实例的

当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
这句话是在网上看到的,写的比较明白,这里说明了几个问题

首先,synchronized是对一个类的实例对象起作用的,什么意思呢,举个例子

public class example1 {    public synchronized void a() {        System.out.println("a");        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    public static void main(String[] args) {        example1 example1=new example1();        Thread1 thread1=new Thread1(example1);        new Thread(thread1).start();        new Thread(thread1).start();    }}class Thread1 implements Runnable{    example1 example1;    public Thread1(example1 example1) {        // TODO Auto-generated constructor stub        this.example1=example1;    }    @Override    public void run() {        // TODO Auto-generated method stub        example1.a();    }   }

这段程序很正常的先输出一个a,然后等了一秒继续输出一个a,一秒后结束程序,把上面的程序做一下修改,只修改main函数即可

附:synchronized锁住对象和修饰方法的原理是一样的,只不过修饰方法的时候默认是锁住调用这个方法的实例,也就相当于synchronized(this)

public static void main(String[] args) {        example1 example1=new example1();        example1 example2=new example1();//这里我们创建了一个新的example1实例        Thread1 thread1=new Thread1(example1);        Thread1 thread2=new Thread1(example2);        new Thread(thread1).start();        new Thread(thread2).start();    }

这段程序的输出结果就很能说明问题了,运行之后一下子输出了两个a,一秒之后结束程序。
为什么会这样,这个原理其实很简单,synchronized上锁的原理在jvm中有讲到过,就是定义了一个int变量,当被锁住的代码访问这个实例的时候会让这个变量+1,释放锁的时候则会减回去,synchronized()锁住对象实例的时候会先去访问该对象的这个变量,如果该变量的值为0的话,当前线程获得该对象的锁,运行代码,否则等待直到该变量的值为0的时候再开始执行

被synchronized修饰的代码和没有被修饰的代码之间不存在上锁等待的问题

举个例子

package card.send;public class example1 {    public synchronized void a() {        System.out.println("a");        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    public void b() {        System.out.println("b");    }    public static void main(String[] args) {        example1 example1=new example1();        Thread1 thread1=new Thread1(example1);        Thread2 thread2=new Thread2(example1);        new Thread(thread1).start();        try {            Thread.sleep(10);//方便让a先执行        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        new Thread(thread2).start();    }}class Thread1 implements Runnable{    example1 example1;    public Thread1(example1 example1) {        // TODO Auto-generated constructor stub        this.example1=example1;    }    @Override    public void run() {        // TODO Auto-generated method stub        example1.a();    }   }class Thread2 implements Runnable{    example1 example1;    public Thread2(example1 example1) {        // TODO Auto-generated constructor stub        this.example1=example1;    }    @Override    public void run() {        // TODO Auto-generated method stub        example1.b();    }   }

这段程序先输出了a,然后直接输出了b。等待一秒程序退出。两个线程用的是用一个实例,可以看出synchronized并不对普通方法作用

总结一下使用synchronized使用的注意事项

  • 确保多个线程共享的实例是同一个实例
  • 不同对象之间不存在竞争关系,他们之间的锁互不干扰
  • -

wait()方法

这是一个非常重要的方法,首先要澄清一下他和sleep方法有着巨大的区别,首先他们就不是一个类里的方法,sleep是Thread里的方法,而wait是Object里的方法。他们的使用方法和原理也有很大差异

首先你要明白wait方法是和synchronized锁一起用的
只有上了锁的方法中才能调用wait方法,否则会立刻抛出InterruptedException异常
上过锁的方法调用wait方法之后会释放锁,让同一个实例的其他用synchronized修饰的方法有执行的机会

package card.send;public class example1 {    public synchronized void a() {        try {            wait();            System.out.println("a");        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    public synchronized void b() {        System.out.println("b");    }    public static void main(String[] args) {        example1 example1=new example1();        Thread1 thread1=new Thread1(example1);        Thread2 thread2=new Thread2(example1);        new Thread(thread1).start();        try {            Thread.sleep(10);        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        new Thread(thread2).start();    }}class Thread1 implements Runnable{    example1 example1;    public Thread1(example1 example1) {        // TODO Auto-generated constructor stub        this.example1=example1;    }    @Override    public void run() {        // TODO Auto-generated method stub        example1.a();    }   }class Thread2 implements Runnable{    example1 example1;    public Thread2(example1 example1) {        // TODO Auto-generated constructor stub        this.example1=example1;    }    @Override    public void run() {        // TODO Auto-generated method stub        example1.b();    }   }

这段代码和之前的也差不多,只是在方法a中加了一个wait方法,运行之后可以看到程序并没有输出a,而是输出b后还一直在等待,这也是一种死锁的问题,这个一会在说,总之从这个例子我们可以看出wait之后,线程会先解锁,然后进入阻塞状态。这也是和sleep的区别之一,sleep方法是即使睡着了,也要抱着锁不放手。

wait之后如何唤醒

方法很简单,Object里面提供了notify和notifyAll两个方法提供给我们唤醒线程,修改一下刚才的b方法

public synchronized void b() {    System.out.println("b");    notify();}

再运行,就可以看到b输出之后就输出了a,b将thread1唤醒了。

wait和notify都是属于对象实例的

wait方法和notify方法不是属于Thread类的而是属于Object类的,他们是让当前实例沉睡和唤醒,修改一下上面例子的main方法

public static void main(String[] args) {        example1 example1=new example1();        example1 example2=new example1();        Thread1 thread1=new Thread1(example1);        Thread2 thread2=new Thread2(example2);        new Thread(thread1).start();        try {            Thread.sleep(10);        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        new Thread(thread2).start();    }

这次再跑程序发现只输出了b,a就是不输出。因为notify和notifyAll只能唤醒同调用同一个实例中wait方法的线程,对于调用其他实例中wait方法导致沉睡的线程notify是无能为力的,这里使用的wait方法和notify方法其实相当于this.wait()和this.notify(),在使用wait和notify之前先搞清楚你锁住的是哪个对象,你要让哪个对象休眠,你要唤醒的沉睡中的线程等待的是哪个对象

原创粉丝点击