threadSynchronized

 

1   多线程安全问题

1.1 起因

在一个进程中,多个线程共享相同的资源(数据或者引用地址),出现的多个线程先后使用、更新或者删除数据造成的重复读,幻读或者脏读问题。

1.2 非安全线程—使用继承Thread类的方式

1.2.1   example

public class SellTicketThread extends Thread{

   int tickets=100;

   public void run(){

      while(tickets>0){

         System.out.println(getName()+"--"+tickets);

         tickets--;

      } 

   }

}

public static void main(String[] args){

      Thread t1=newSellTicketThread();

      Thread t2=newSellTicketThread();

      Thread t3=newSellTicketThread();

      Thread t4=newSellTicketThread();

      t1.setName("窗口1");

      t2.setName("窗口2");

      t3.setName("窗口3");

      t4.setName("窗口4");

      t1.start();

      t2.start();

      t3.start();

      t4.start();

   }

1.2.2解决方案

简单的多线程共享基本数据类型的数据时,使用线程子类内静态化属性的方法可以解决一部分重复读的问题,但是不能解决线程首次并发时的重复读问题。

public class SellTicketThread extends Thread{

   public static int tickets=100;

   public void run(){

      while(tickets>0){

         System.out.println(getName()+"--"+tickets);

         try {

            Thread.sleep(10);

         } catch (InterruptedExceptione) {

            e.printStackTrace();

         }

         tickets--;

      } 

   }

}

1.3 使用实现Runnable接口的方式

1.3.1   example   

简单的多线程共享基本数据类型的数据时,使用实现Runnable接口的方式时,由于线程只是实现了run方法,采用的是Runnable实现类的全局变量,而不是像实现Thread类的方式继承了Thread的属性,能够保持共享属性的完全隔离性(除首次开启线程时)。那么,采用实现接口的方式,在使用这个全局变量时,可能存在重复读,脏读,幻读等安全问题。

public class SellTicketTask implements Runnable{

   private static int ticket=100;

   public void run() {

      while(ticket>0){

         try {

            Thread.sleep(10);

         } catch (InterruptedExceptione1) {

            // TODO Auto-generatedcatch block

            e1.printStackTrace();

         }

         System.out.println(Thread.currentThread().getName()+"--"+ticket);

         try {

            Thread.sleep(10);

         } catch (InterruptedExceptione) {

            // TODO Auto-generatedcatch block

            e.printStackTrace();

         }

         ticket--;

      }

   }

}

1.3.2 解决方案

(1)      使用同步代码块

Synchronized(Object){

需要同步的代码

}

public class SellTicketTask implements Runnable{

   private int ticket=100;

   Object o=new Object();

   public void run() {

      while(ticket>0){

         synchronized(o){

            if(ticket>0){

               System.out.println(Thread.currentThread().getName()+"--"+ticket);

               try {

                  Thread.sleep(10);

               } catch (InterruptedExceptione) {

                  e.printStackTrace();

               }

               ticket--;

            }

         }

      }

   }

}

对于synchronized的锁对象的测试

public class SellTicketTask implements Runnable{

   private int ticket=100;

   Object o=new Object();

   Object o2=new Object();

   int a=1;

   public void run() {

      while(ticket>0){

         if(a==1){

            synchronized(o){

               if(ticket>0){

                  System.out.println(Thread.currentThread().getName()+"-1-"+ticket);

                  try {

                     Thread.sleep(10);

                  } catch (InterruptedExceptione) {

                     e.printStackTrace();

                  }

                  ticket--;

                  a=2;

               }

            }

         }

         if(a==2){

            synchronized(o){

               if(ticket>0){

                  System.out.println(Thread.currentThread().getName()+"-2-"+ticket);

                  try {

                     Thread.sleep(10);

                  } catch (InterruptedExceptione) {

                     e.printStackTrace();

                  }

                  ticket--;

                  a=3;

               }

            }

         }

         if(a==3){

            synchronized(o){

               if(ticket>0){

                  System.out.println(Thread.currentThread().getName()+"-3-"+ticket);

                  try {

                     Thread.sleep(10);

                  } catch (InterruptedExceptione) {

                     e.printStackTrace();

                  }

                  ticket--;

                  a=1;

               }

            }

         }



      }

   }

}

(2) 使用synchronized修饰非静态方法

如果一个方法内代码全部被synchronized代码块包裹,那么这个方法就可以定义为一个同步方法。格式将sychronized加到这个方法的修饰中。由于同步方法的锁就是当前的对象。所有,如果synchronized代码块和同步方法混合使用,需要将synchronized的对象锁设置为当前对象,也就是this。

public class SellTicketTask implements Runnable{

   private int ticket=100;

   Object o=new Object();

   Object o2=new Object();

   int a=1;

   public synchronized void synmethod(inta){

      if(ticket>0){

         System.out.println(Thread.currentThread().getName()+"-"+a+"-"+ticket);

         try {

            Thread.sleep(10);

         } catch(InterruptedExceptione) {

            e.printStackTrace();

         }

         ticket--;

         if(a==2){

            this.a=3;

         }

         if(a==3){

            this.a=1;

         }

      }

   }

   public void run() {

      while(ticket>0){

         if(a==1){

            synchronized(this){

               if(ticket>0){

                  System.out.println(Thread.currentThread().getName()+"-1-"+ticket);

                  try {

                     Thread.sleep(10);

                  } catch (InterruptedExceptione) {

                     e.printStackTrace();

                  }

                  ticket--;

                  a=2;

               }

            }

         }

         if(a==2){

            synmethod(2);

         }

         if(a==3){

            synmethod(3);

         }



      }

   }

}

(3) 使用synchronized修饰的静态方法

同步静态方法的格式同同步非静态方法,但是注意引用全局变量的修饰符和类属性使用类调用的问题。另外,静态方法的对象锁不是类的实例对象,因为静态方法的加载先于实例对象。那么,一般,类加载到内存中,最开始是将class文件字节码内容加载到内存,并将这些静态数据转换成方法区中的运行时数据结构,然后在堆中生成一个表示这个类的Class对象。一般,有三种方法得到这个加载类的Class对象。1,Object类的getClass方法;2,类名.class;3,Class类的forName方法。

public class SellTicketTask implements Runnable{

   private static int ticket=100;

   Object o=new Object();

   Object o2=new Object();

   static int a=1;

   public static synchronized void stasynmethod(inta){

      if(ticket>0){

         System.out.println(Thread.currentThread().getName()+"-"+a+"-"+ticket);

         try {

            Thread.sleep(10);

         } catch(InterruptedExceptione) {

            e.printStackTrace();

         }

         ticket--;

         if(a==2){

            SellTicketTask.a=3;

         }

         if(a==3){

            SellTicketTask.a=1;

         }

      }

   }

   public void run() {

      while(ticket>0){

         if(a==1){

            //synchronized(SellTicketTask.class){

            try {

               synchronized(Class.forName("com.edu.thread.SellTicketTask")){

               //synchronized(newSellTicketTask().getClass()){

                //使用3种方法得到运行类的class对象

                  if(ticket>0){

                     System.out.println(Thread.currentThread().getName()+"-1-"+ticket);

                     try {

                        Thread.sleep(10);

                     } catch(InterruptedExceptione) {

                        e.printStackTrace();

                     }

                     ticket--;

                     a=2;

                  }

               }

            } catch (ClassNotFoundExceptione) {

               e.printStackTrace();

            }

         }

         if(a==2){

            stasynmethod(2);

         }

         if(a==3){

            stasynmethod(3);

         }

      }

   }

}

2   利用锁解决懒汉式单例的多线程安全问题

2.1 单例模式原型

public class SingleHungry {

   private SingleHungrysingleH=new SingleHungry();

   private SingleHungry(){}

   public static SingleHungrygetSingleH(){

      return singleH;

   }

}
public class SingleLazy {

   private static SingleLazy singleL;

   private SingleLazy(){}

   public static SingleLazy getSingleL(){

     

      if(singleL==null){

         singleL=new SingleLazy();

      }

      returnsingleL;

   }

}

2.2 人为制造懒汉式的线程安全问题

public class SingleLazy {

   private static SingleLazy singleL;

   private SingleLazy(){}

   public static SingleLazy getSingleL(){

      if(singleL==null){

         try {

            Thread.sleep(100);

         } catch(InterruptedExceptione) {

            e.printStackTrace();

         }

         singleL=new SingleLazy();

      }

      return singleL;

   }

}
public class TestSingle implements Runnable{

   public void run() {

   System.out.println(Thread.currentThread().getName()+"--"+SingleLazy.getSingleL());

   }

}

public static void main(String[] args) throws InterruptedException{

      TestSinglets=new TestSingle();

      Threadt1=new Thread(ts,"lizi1");

      Threadt2=new Thread(ts,"lizi2");

      Threadt3=new Thread(ts,"lizi3");

      Threadt4=new Thread(ts,"lizi4");

      Threadt5=new Thread(ts,"lizi5");

      Threadt6=new Thread(ts,"lizi6");

      Threadt7=new Thread(ts,"lizi7");

      Threadt8=new Thread(ts,"lizi8");

      t1.start();

      t2.start();

      t3.start();

      t4.start();

      t5.start();

      t6.start();

      t7.start();

      t8.start();

   }

2.3  利用同步锁解决线程安全问题

public class SingleLazy {

   private static SingleLazy singleL;

   private SingleLazy(){}

   public static SingleLazy getSingleL(){

       //优先判断null,提高效率。

      if(singleL==null){

         synchronized(SingleLazy.class){

            if(singleL==null){

               try {

                  Thread.sleep(100);

               } catch(InterruptedExceptione) {

                  e.printStackTrace();

               }

               singleL=new SingleLazy();

            }

         }

      }

      return singleL;

   }

}

测试方法同上。

3   deadlock

3.1 什么是死锁

两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象。若无其他作用,它们都将无法推进下去。

产生死锁的四个条件:1,互斥条件;2,请求与保持条件;3,不剥夺条件;4,循环等待条件。

3.2 example

public class DeadLock implements Runnable{

   private Objecto1=new Object();

   private Objecto2=new Object();

   public void run() {

      while(true){

         synchronized(o1){

            System.out.println(Thread.currentThread().getName()+"--"+"拿刀");

            synchronized(o2){

               System.out.println(Thread.currentThread().getName()+"--"+"拿叉");

            }

         }

         synchronized(o2){

            System.out.println(Thread.currentThread().getName()+"--"+"拿叉");

            synchronized(o1){

               System.out.println(Thread.currentThread().getName()+"--"+"拿刀");

            }

         }

      }

   }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值