• 女朋友也能看懂的多线程同步


    1、什么是线程安全?

            当多个线程同时共享同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。

            举个案例来说:现在有100张火车票,有两个窗口同时抢火车票,使用多线程模拟抢票效果。

    /**
     * @classDesc: 功能描述:(多线程之买火车票案例-展示线程不安全问题)
     */
    class ThreadTrain implements Runnable {
    	// 这是货票总票数,多个线程会同时共享资源
    	private int trainCount = 100;
    
    	@Override
    	public void run() {
    	    while (trainCount > 0) { //循环是指线程不停的去卖票
                try {
    		// 等待100毫秒
    	        Thread.sleep(10);
    	        } catch (InterruptedException e) {
                    e.printStackTrace();
    	    }
    	    sale();
    	    }
    	}
    	/**
    	 * @methodDesc: 功能描述:(出售火车票)
    	 */
    	public void sale() {
    	    if (trainCount > 0) {
                try {
    		Thread.sleep(10);
    	    } catch (Exception e) {
                    e.printStackTrace();
    	    }
    	   System.out.println(Thread.currentThread().getName() + "出售第" + (100 - trainCount + 1) + "张票");
    	   trainCount--;		
               }
         }
    }
    
    public class ThreadDemo {
    	public static void main(String[] args) {
                //创建一个实例
    	    ThreadTrain threadTrain = new ThreadTrain(); 
                //创建多个线程必须要用同一个实例,因为要共享全局变量
    	    Thread thread1 = new Thread(threadTrain, "一号窗口");
    	    Thread thread2 = new Thread(threadTrain, "二号窗口");
    	    thread1.start();
    	    thread2.start();
    	}
    }

    运行结果:

    一号窗口和二号窗口同时出售第一张和第七张火车票,部分火车票会重复出售。

    结论发现多个线程共享同一个全局成员变量时,做写的操作可能会发生数据冲突问题。

    2、如何解决多线程之间线程安全问题?

           使多线程之间同步或使用锁(lock)

    3、为什么使用线程同步或使用锁能解决线程安全问题呢?

           将可能会发生数据冲突问题(线程不安全问题)的代码,只让当前一个线程进行执行。代码执行完成后释放锁,释放之后才能让其他线程执行,这样就可以解决线程不安全问题。

    4、什么是多线程之间同步?

          当多个线程共享同一个资源,而不会受到其他线程的干扰。

    5、我们该如何解决多线程安全问题?

           第一种:使用同步代码块

           什么是同步代码块?

           同步代码块就是将可能会发生线程安全问题的代码给包裹起来

    private Object obj = new Object();// 自定义多线程同步锁
    	public void sale() {
            synchronized (obj ) {
    	if (trainCount > 0) {
            try {
    	    Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "出售第" + (100 - trainCount + 1) + "张票");
            trainCount--;			
           }
         }
       }

           第二种:使用同步函数

           什么是同步函数?

           使用synchronized 修饰的函数称为同步函数

    public synchronized void sale() {
        if (trainCount > 0) { 
        try {
    	Thread.sleep(40);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "出售第" +(100 - trainCount + 1) + "张票");
        trainCount--;
        }
    }

           动脑筋思考一下,同步函数使用的是什么锁?怎样证明呢?

           同步函数使用this锁

           证明方式: 一个线程使用同步代码块(this明锁),另一个线程使用同步函数。如果两个线程能实现同步,说明同步函数使用的是this锁。

          第三种:使用静态同步函数

          什么是静态同步函数?

          同步函数上加上static关键字修饰或者使用线程类.class文件字节码

    public static synchronized void sale() {
        if (trainCount > 0) {
        try {
            Thread.sleep(40);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "出售第" + (100 - trainCount + 1) + "张票");
        trainCount--;
        }
    }

        再动脑筋思考一下,同步函数使用的是什么锁?怎样证明呢? 

        静态同步函数使用的锁是当前线程类的字节码文件

       证明方式: 一个线程使用当前线程类的字节码文件,另一个线程使用静态同步函数。如果两个线程能实现同步,说明静态同步函数使用的锁是当前线程类的字节码文件。

    6、多线程死锁问题

        什么是多线程死锁?

        同步中嵌套同步,锁没有来得及释放,一直等待,就会导致死锁。

        下面就拿买火车票来演示一下死锁问题:

    class ThreadTrain implements Runnable {
    	// 这是总票数,多个线程会同时共享资源
    	private int trainCount = 100;
    	public boolean flag = true;
    	private Object obj= new Object();
    
    	@Override
    	public void run() {
            if (flag) {
    	    while (trainCount > 0) {
                synchronized (obj) {
    	    //锁(同步代码块)在什么时候释放? 代码执行完会自动释放锁
    	    //如果flag为true 先拿到 obj锁,再拿到this锁才能执行
    	    //如果flag为false先拿到this,再拿到obj锁才能执行
                sale();
                }
    	  }
            } else {
                while (trainCount > 0) {
                sale();
                }
            }
        }
    	/**
    	 * @methodDesc: 功能描述:(出售火车票)
    	 */
    	public synchronized void sale() {
                synchronized (obj) {
    		if (trainCount > 0) {
                    try {
    		    Thread.sleep(40);
    		} catch (Exception e) { 
                        e.printStackTrace();
                    }
                 System.out.println(Thread.currentThread().getName() + "出售第" + (100 - trainCount + 1) + "张票");
    	     trainCount--;
                }
            }
        }
    }
    
    public class DeadlockThread {
    	public static void main(String[] args) throws InterruptedException {
    	ThreadTrain threadTrain = new ThreadTrain(); // 创建一个实例
    	Thread thread1 = new Thread(threadTrain, "一号窗口");
    	Thread thread2 = new Thread(threadTrain, "二号窗口");
            //此时flag=true,线程thread1执行synchronized代码块,拿到obj锁
    	thread1.start();
            //让main函数主线程休眠40ms
    	Thread.sleep(40);
    	threadTrain.flag = false;
            //此时flag=false,线程thread2执行synchronized函数,拿到this锁
    	thread2.start();
    	}
    }
    //此时线程thread1需要线程thread2的this锁,线程thread2需要线程thread1的obj锁,互相要锁,并且互相都不释放,就会产生死锁问题。

         那么怎样可以避免死锁呢?

         最好的方法就是不要在同步中嵌套同步

  • 相关阅读:
    The Network Adapter could not establish the connection问题研究
    条件更新或插入
    如何解决Oracle临时表空间过大
    oracle的临时表空间写满磁盘空间解决改问题的步骤
    如何在oracle中缩小临时表空间?ORA-01652无法在表空间中扩展temp
    oracle mysql sql 根据一张表更新另一张表
    Don’t Repeat Yourself,Repeat Yourself
    mysql oracle sql获取树 父级 子级 及自己
    hutool BigExcelWriter 下的autoSizeColumnAll异常问题
    Java手写数组栈
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13309330.html
Copyright © 2020-2023  润新知