• Java多线程——线程安全问题


    一、什么情况下会产生线程安全问题?

    同时满足以下两个条件时:

    1,多个线程在操作共享的数据。
    2,操作共享数据的线程代码有多条。

    当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。

    例1:四个线程卖100张票

    public class TicketDemo implements Runnable {
    	private int tickets = 100;
    
    	public void run() {
    		while (true) {
    
    			if (tickets > 0) {
    				try {
    					Thread.sleep(10);
    				} catch (InterruptedException e) {
    				}
    				System.out.println(Thread.currentThread().getName() + "....sale:...." + tickets--);
    			}
    		}
    	}
    
    	public static void main(String[] args) {
    		TicketDemo ticketDemo = new TicketDemo();
    		Thread t1 = new Thread(ticketDemo);
    		Thread t2 = new Thread(ticketDemo);
    		Thread t3 = new Thread(ticketDemo);
    		Thread t4 = new Thread(ticketDemo);
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    
    Thread-3....sale....100
    Thread-2....sale....99
    Thread-0....sale....97
    Thread-1....sale....98
    Thread-3....sale....96
    Thread-1....sale....94
    Thread-0....sale....94
    Thread-2....sale....95
    Thread-1....sale....93
    Thread-0....sale....92
    Thread-2....sale....92
    Thread-3....sale....92
    Thread-0....sale....91
    Thread-2....sale....89
    Thread-3....sale....90
    Thread-1....sale....91
    Thread-1....sale....88
    Thread-3....sale....86
    Thread-0....sale....88
    Thread-2....sale....87
    Thread-2....sale....84
    Thread-3....sale....84
    Thread-1....sale....85
    Thread-0....sale....83
    Thread-1....sale....82
    Thread-0....sale....80
    Thread-3....sale....79
    Thread-2....sale....81
    Thread-3....sale....78
    Thread-2....sale....75
    Thread-1....sale....76
    Thread-0....sale....77
    Thread-2....sale....74
    Thread-1....sale....71
    Thread-0....sale....73
    Thread-3....sale....72
    Thread-1....sale....70
    Thread-0....sale....68
    Thread-3....sale....69
    Thread-2....sale....67
    Thread-2....sale....66
    Thread-3....sale....64
    Thread-0....sale....63
    Thread-1....sale....65
    Thread-2....sale....62
    Thread-0....sale....62
    Thread-1....sale....60
    Thread-3....sale....61
    Thread-2....sale....59
    Thread-0....sale....57
    Thread-3....sale....58
    Thread-1....sale....59
    Thread-0....sale....56
    Thread-1....sale....56
    Thread-3....sale....55
    Thread-2....sale....56
    Thread-1....sale....54
    Thread-2....sale....54
    Thread-0....sale....54
    Thread-3....sale....53
    Thread-0....sale....52
    Thread-3....sale....52
    Thread-2....sale....50
    Thread-1....sale....51
    Thread-2....sale....49
    Thread-0....sale....49
    Thread-3....sale....48
    Thread-1....sale....48
    Thread-2....sale....46
    Thread-0....sale....44
    Thread-3....sale....45
    Thread-1....sale....47
    Thread-1....sale....43
    Thread-0....sale....42
    Thread-2....sale....42
    Thread-3....sale....41
    Thread-1....sale....40
    Thread-0....sale....39
    Thread-3....sale....39
    Thread-2....sale....40
    Thread-2....sale....38
    Thread-1....sale....37
    Thread-3....sale....35
    Thread-0....sale....36
    Thread-3....sale....34
    Thread-1....sale....33
    Thread-0....sale....32
    Thread-2....sale....31
    Thread-3....sale....30
    Thread-1....sale....29
    Thread-0....sale....29
    Thread-2....sale....28
    Thread-3....sale....27
    Thread-0....sale....25
    Thread-1....sale....26
    Thread-2....sale....24
    Thread-1....sale....23
    Thread-0....sale....23
    Thread-3....sale....22
    Thread-2....sale....21
    Thread-1....sale....20
    Thread-3....sale....20
    Thread-0....sale....20
    Thread-2....sale....19
    Thread-3....sale....16
    Thread-0....sale....17
    Thread-1....sale....18
    Thread-2....sale....15
    Thread-0....sale....13
    Thread-1....sale....12
    Thread-3....sale....14
    Thread-2....sale....11
    Thread-3....sale....10
    Thread-0....sale....8
    Thread-1....sale....9
    Thread-2....sale....7
    Thread-3....sale....6
    Thread-0....sale....5
    Thread-1....sale....4
    Thread-2....sale....3
    Thread-1....sale....2
    Thread-3....sale....2
    Thread-2....sale....1
    Thread-0....sale....2
    运行结果

      观察结果,我们发现会有多个线程卖到同一张票和卖到0号票的情况,这就是线程安全问题。


    解决思路:

    将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算。

    当前线程把这些代码都执行完毕后,其他线程才可以参与运算。

    在java中,用同步代码块就可以解决这个问题。

    同步代码块的格式:
    synchronized(对象)
    {
    需要被同步的代码 ;
    }

    这个对象一般称为同步锁

    同步的前提:同步中必须有多 个线程并使用同一个锁。

    同步的好处:解决了线程的安全问题。

    同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。

    解决例1的线程安全问题代码:

    public class TicketDemo implements Runnable {
    	private int tickets = 100;
    	Object obj = new Object();
    
    	public void run() {
    		while (true) {
    			synchronized (obj) {
    				if (tickets > 0) {
    					try {
    						Thread.sleep(10);
    					} catch (InterruptedException e) {
    					}
    					System.out.println(Thread.currentThread().getName() + "....sale...." + tickets--);
    				}
    			}
    		}
    	}
    
    	public static void main(String[] args) {
    		TicketDemo ticketDemo = new TicketDemo();
    		Thread t1 = new Thread(ticketDemo);
    		Thread t2 = new Thread(ticketDemo);
    		Thread t3 = new Thread(ticketDemo);
    		Thread t4 = new Thread(ticketDemo);
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    

      


    二、同步锁是什么:

    同步函数使用的锁是 this

    静态的同步函数使用的锁是该函数所属 字节码文件对象 ,可以用 getClass()方法获取,也可以用 当前类名.class  表示。

    同步函数和同步代码块的区别:

    同步函数的锁是固定的this。

    同步代码块的锁是任意的对象。

    建议使用同步代码块。

     1 class Ticket implements Runnable {
     2     private static int num = 100;
     3     boolean flag = true;
     4 
     5     public void run() {
     6         if (flag)
     7             while (true) {
     8                 synchronized (Ticket.class)//(this.getClass())同步代码块
     9                 {
    10                     if (num > 0) {
    11                         try {
    12                             Thread.sleep(10);
    13                         } catch (InterruptedException e) {
    14                         }
    15                         System.out.println(Thread.currentThread().getName() + ".....obj...." + num--);
    16                     }
    17                 }
    18             }
    19         else
    20             while (true)
    21                 this.show();
    22     }
    23 
    24     public static synchronized void show()//同步函数
    25     {
    26         if (num > 0) {
    27             try {
    28                 Thread.sleep(10);
    29             } catch (InterruptedException e) {
    30             }
    31             System.out.println(Thread.currentThread().getName() + ".....function...." + num--);
    32         }
    33     }
    34 }
    35 
    36 class StaticSynFunctionLockDemo {
    37     public static void main(String[] args) {
    38         Ticket t = new Ticket();
    39         
    40         Thread t1 = new Thread(t);
    41         Thread t2 = new Thread(t);
    42 
    43         t1.start();
    44         try {
    45             Thread.sleep(10);
    46         } catch (InterruptedException e) {
    47         }
    48         t.flag = false;
    49         t2.start();
    50     }
    51 }
    View Code

     


    三、死锁常见情况:

    同步嵌套时,两个线程你拿了我的锁,我拿了你的锁,都不释放,造成死锁。

    可以记一套死锁情况代码,面试可能用得到。

    死锁情况:

    class Testa implements Runnable {
    	private boolean flag;
    	Testa(boolean flag) {
    		this.flag = flag;
    	}
    	public void run() {
    		if (flag) {
    			while (true)
    				synchronized (MyLock.locka) {
    					System.out.println(Thread.currentThread().getName() + "..if   locka....");
    					synchronized (MyLock.lockb) {
    						System.out.println(Thread.currentThread().getName() + "..if   lockb....");
    					}
    				}
    		} else {
    			while (true)
    				synchronized (MyLock.lockb) {
    					System.out.println(Thread.currentThread().getName() + "..else  lockb....");
    					synchronized (MyLock.locka) {
    						System.out.println(Thread.currentThread().getName() + "..else   locka....");
    					}
    				}
    		}
    	}
    }
    class MyLock {
    	public static final Object locka = new Object();
    	public static final Object lockb = new Object();
    }
    class DeadLockTest {
    	public static void main(String[] args) {
    		Testa a = new Testa(true);
    		Testa b = new Testa(false);
    		Thread t1 = new Thread(a);
    		Thread t2 = new Thread(b);
    		t1.start();
    		t2.start();
    	}
    }
    

      


    四、单例设计模式中的线程安全问题

    //饿汉式
    class Single
    {
    	private static final Single s = new Single();
    	private Single(){}
    	public static Single getInstance()
    	{
    		return s;
    	}
    }
    
    //懒汉式
    /*
    *加入同步是为了解决多线程安全问题。
    *
    *加入双重判断不用每次都判断是否上锁,是为了解决效率问题。
    **/
    
    class Single
    {
    	private static Single s = null;
    	private Single(){}
    	public static Single getInstance()
    	{
    		if(s==null)
    		{
    			synchronized(Single.class)		
    			{
    				if(s==null)
    		//				-->0 -->1
    					s = new Single();
    			}
    		}
    		return s;
    	}
    }
    

    开发用饿汉式,没有线程安全问题。——饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

    面试懒汉式,记住如何解决线程安全问题。

  • 相关阅读:
    多测师讲解python _练习题002_高级讲师肖sir
    多测师讲解python _练习题002_高级讲师肖sir
    多测师讲解python _类(原始版)_高级讲师肖sir
    多测师讲解python _练习题003_高级讲师肖sir
    多测师讲解python _re模块_高级讲师肖sir
    多测师讲解python_os模块_高级讲师肖sir
    多测师讲解python _string_高级讲师肖sir
    MySQL介绍
    linux平台mysql密码设破解
    linux平台mysql密码设置
  • 原文地址:https://www.cnblogs.com/kkkky/p/7754383.html
Copyright © 2020-2023  润新知