线程的同步

如果有读者看了上一篇文章的栗子会出现线程安全的问题,如果还没有看上篇请点击多线程详解,这一篇是承接上一篇接着讲的。
那为什么会出现线程安全问题呢?原因是:由于一个线程在操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来了,导致共享数据存在了安全问题。解决的办法:必须让一个线程操作共享数据完毕后,其它线程才有机会参与共享数据的操作。这就出现了线程的同步机制。

同步代码块


  synchronized(同步监视器){

  //需要被同步的代码块(即为操作共享数据的代码)

  }

  1.共享数据:多个线程共同操作的同一个数据(变量)

  2.同步监视器(锁):由任何类的对象来充当,那个线程获取此监视器,就执行大括号里被同步的代码。

  3.要求所有线程必须共用同一把锁。

  4.在实现Runnnable中,考虑同步可以使用this当锁。但是在继承Thread中慎用this当锁。

利用同步代码块修改实现Runnable上节栗子中出现安全问题


class SubThread implements Runnable{

  int ticket=100; 

  Object obj=new Object();

  public void run(){

   Object obj=new Object();//局部变量,放进来的话就没效果

  while (true){

  synchronized (this){//当前对象,即为st

  if(ticket>0){

  try{

  Thread.currentThread().sleep(1000);

  }catch (Exception e){}

   System.out.print(Thread.currentThread().getName()+"已售出票号:"+ticket--);

  }

  }

  }

  }

}

 class TestThread{

  public static void main(String []args){

  SubThread st=new SubThread();

  Thread th1=new Thread(st);

  Thread th2=new Thread(st);

  Thread th3=new Thread(st);

  th1.start();

  th2.start();

  th3.start();

 }

 }

注意:在代码块中的锁的对象不能在run()方法里创建,否则3个线程都会创建锁。要求所有线程必须共用同一把锁。

利用同步代码块修改继承Thread上节栗子中出现安全问题


class SubThread extends Thread{

  static int ticket=100; 

  static Object object=new Object();

  public void run(){

  while (true){

 //synchronized (this){//当前对象,为st1,st2,st3。所以在继承中锁为this是没效果的,也会出现安全问题。改为

  synchronized (Object){ 

  if(ticket>0){

  try{

  Thread.currentThread().sleep(1000);

  }catch (Exception e){}

   System.out.print(Thread.currentThread().getName()+"已售出票号:"+ticket--);

  }

  }

  }

  }

}

 class TestThread{

  public static void main(String []args){

   SubThread st1=new SubThread();

    SubThread st2=new SubThread();

    SubThread st3=new SubThread();

    st1.start();

    st2.start();

    st3.start();

 }

 }

注意:在继承Thread中,慎用this。

同步方法

  • 将操作(共享数据)的方法声明为synchronized,即此为同步方法,能够保证其中一个线程执行此方法时,

其它线程在外等待直至此线程完成。

  • 同步方法的锁为:this。

利用同步方法修改实现Runnable上节栗子中出现安全问题


class SubThread implements Runnable{

  int ticket=100; 

  public void run(){

  while (true){

  show();  //当前对象的show方法

  }

  }

  public synchronized void  show(){

    if(ticket>0){

      try{

      Thread.currentThread().sleep(1000);

      }catch (Exception e){}

       System.out.print(Thread.currentThread().getName()+"已售出票号:"+ticket--);

      }

  }

}

 class TestThread{

  public static void main(String []args){

  SubThread st=new SubThread();

  Thread th1=new Thread(st);

  Thread th2=new Thread(st);

  Thread th3=new Thread(st);

  th1.start();

  th2.start();

  th3.start();

 }

 }

不能利用同步方法修改继承Thread上节栗子中出现安全问题


class SubThread extends Thread{

  static int ticket=100; 

  static Object object=new Object();

  public void run(){

  while (true){

   //show();   //这种创建3个对象,也会出现安全问题

   synchronized (object){

     if(ticket>0){

            try{

            Thread.currentThread().sleep(1000);

            }catch (Exception e){}

             System.out.print(Thread.currentThread().getName()+"已售出票号:"+ticket--);

            } 

   }

  }

  }

  public synchronized void  show(){ //this充当的锁,表示的是st1,st2,st3if(ticket>0){

        try{

        Thread.currentThread().sleep(1000);

        }catch (Exception e){}

         System.out.print(Thread.currentThread().getName()+"已售出票号:"+ticket--);

        }

    }

}

 class TestThread{

  public static void main(String []args){

   SubThread st1=new SubThread();

    SubThread st2=new SubThread();

    SubThread st3=new SubThread();

    st1.start();

    st2.start();

    st3.start();
    //解决此同步方法,相当于只创建一个对象
    new Thread(st1).start();
    new Thread(st1).start();
    new Thread(st1).start();
 }

 }

注意:同步方法不能使用在继承Thread(在多个线程),因为上个例子中show()方法是this充当的锁。对于同步方法需要特别注意。

补充(单例懒汉式线程安全)

class Singleton{
 private static Singleton instance=null;
 public static Singleton getInstance(){
  if(instance==null){
    synchronized(Singleton.class){
     if(instance==null){
      instance=new Singleton();
     }
    }
  }
  return instance;
 }
}

线程的死锁

产生的原因: 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。也就相当于分别都占用了对方的资源,并且双方都没有放弃。
解决的办法:需要专门的算法、原则。尽量减少同步资源的定义。
举个例子:

 class TestThread{
    static StringBuffer sb1=new StringBuffer();
    static StringBuffer sb2=new StringBuffer();
  public static void main(String []args){
   new Thread(){
            @Override
            public void run() {
                synchronized (sb1){
                    try {
                        Thread.currentThread().sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    sb1.append("A");
                    synchronized (sb2){
                        sb2.append("B");
                    }
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                synchronized (sb2){
                    try {
                        Thread.currentThread().sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                     sb1.append("C");
                    synchronized (sb1){
                        sb2.append("D");
                    }
                }
            }
        }.start();
    }
  }

如果哪些地方有疑问请在下方留言,下一篇讲解线程之间的通信 线程之间通信,附上我的github地址 悟静,欢迎各位Star;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值