threadSynchronized

xiaoxiao2021-02-27  241

 

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()+"--"+"拿刀");             }          }       }    } }

 

转载请注明原文地址: https://www.6miu.com/read-9735.html

最新回复(0)