浅谈多线程之间实现同步(线程安全)

一、为什么有线程安全问题存在?

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

二、线程不安全问题怎么解决?

使用synchronized、jdk1.5并发包lock

三、synchronizd的使用方法

  1. 使用同步代码块
    就是将可能发生线程安全问题的代码,给包裹起来。
    //“同一个数据”部分相当于一把锁,可以使用空的Object对象作为入参传入
    synchronized(同一个数据){
    可能会发生线程冲突问题
    }
    注意:synchronized包裹的部分一定要是可能会发生线程安全问题的代码,而且synchrozied关键字只适合单个jvm,分布式集群环境下不能使用,在分布式集群环境下要使用分布式锁zookeeper。
    package chauncy.threadtrain;
    
    class ThreadTrain implements Runnable {
    	// 火车票总数
    	private int count = 100;
    	//两个线程一定要用同一把锁
    	private Object obj=new Object();
    
    	@Override
    	public void run() {
    		while (count > 0) {
    			try {
    				Thread.sleep(40);
    			} catch (InterruptedException e) {
    			}
    			synchronized(obj){
    				// 100总数减去现有数量count加1为当前出售的第几张票
    				System.out.println(Thread.currentThread().getName() + "出售第" + (100 - count + 1) + "张票");
    				count--;
    			}
    		}
    	}
    }
    
    /**
     * 什么是线程不安全 
     * 当多个线程同时操作同一个共享的全局变量,可能会受到其他线程的干扰。会发生数据冲突的问题。
     * 线程不安全问题怎么解决?
     * 使用synchronize、jdk1.5的并发包lock
     * 使用synchronized关键字包裹起来的代码
     * 每次只能让当前一个线程进行执行
     * @classDesc: 功能描述(模拟线程不安全问题)
     * @author: ChauncyWang
     * @version: 1.0
     */
    public class ThreadDemo1 {
    	public static void main(String[] args) {
    		//线程类一定要用一个实例,因为要重现变量共享的问题
    		ThreadTrain threadTrain = new ThreadTrain();
    		// 1.创建两个线程
    		Thread thread1 = new Thread(threadTrain);
    		Thread thread2 = new Thread(threadTrain);
    		thread1.start();
    		thread2.start();
    	}
    }
    
  2. 使用同步函数(同步方法)
    什么是同步函数?
    在方法上修饰synchronized称为同步函数。
    同步函数用的是什么锁?
    同步函数使用this锁。
    package chauncy.threadtrain;
    
    class ThreadTrain2 implements Runnable {
    	// 火车票总数
    	private int count = 100;
    	//两个线程一定要用同一把锁
    	private Object obj = new Object();
    
    	@Override
    	public void run() {
    		while (count > 0) {
    			show();
    		}
    	}
    
    	public synchronized void show() {
    		if (count > 0) {
    			try {
    				Thread.sleep(4);
    			} catch (InterruptedException e) {
    			}
    			// 100总数减去现有数量count加1为当前出售的第几张票
    			System.out.println(Thread.currentThread().getName() + "出售第" + (100 - count + 1) + "张票");
    			count--;
    		}
    	}
    }
    
    /**
     * @classDesc: 功能描述(使用同步函数(同步方法))
     * @author: ChauncyWang
     * @version: 1.0
     */
    public class ThreadDemo2 {
    	public static void main(String[] args) {
    		//线程类一定要用一个实例,因为要重现变量共享的问题
    		ThreadTrain2 threadTrain2 = new ThreadTrain2();
    		// 1.创建两个线程
    		Thread thread1 = new Thread(threadTrain2);
    		Thread thread2 = new Thread(threadTrain2);
    		thread1.start();
    		thread2.start();
    	}
    }
    
    package chauncy.threadtrain;
    
    class ThreadTrain3 implements Runnable {
    	// 火车票总数
    	private int count = 100;
    	//两个线程一定要用同一把锁
    	private Object obj = new Object();
    	public boolean flag=true;
    
    	@Override
    	public void run() {
    		//线程1 flag为true 线程2 flag为false
    		if(flag){
    			while (count > 0) {
    				//入参为obj情况下线程不安全,为this情况线程安全,证明同步函数使用的是this锁。
    				synchronized(this){
    					if (count > 0) {
    						try {
    							Thread.sleep(4);
    						} catch (InterruptedException e) {
    						}
    						// 100总数减去现有数量count加1为当前出售的第几张票
    						System.out.println(Thread.currentThread().getName() + "出售第" + (100 - count + 1) + "张票");
    						count--;
    					}
    				}
    			}
    		}else{
    			while (count > 0) {
    				show();
    			}
    		}
    	}
    
    	public synchronized void show() {
    		if (count > 0) {
    			try {
    				Thread.sleep(4);
    			} catch (InterruptedException e) {
    			}
    			// 100总数减去现有数量count加1为当前出售的第几张票
    			System.out.println(Thread.currentThread().getName() + "出售第" + (100 - count + 1) + "张票");
    			count--;
    		}
    	}
    }
    
    /**
     * @classDesc: 功能描述(证明同步函数是this锁)
     * @author: ChauncyWang
     * @version: 1.0
     */
    public class ThreadDemo3 {
    	public static void main(String[] args) throws InterruptedException {
    		//线程类一定要用一个实例,因为要重现变量共享的问题
    		ThreadTrain3 threadTrain3 = new ThreadTrain3();
    		// 1.创建两个线程
    		Thread thread1 = new Thread(threadTrain3);
    		Thread thread2 = new Thread(threadTrain3);
    		thread1.start();
    		Thread.sleep(10);
    		threadTrain3.flag=false;
    		thread2.start();
    	}
    }
    
  3. 使用静态同步函数
    什么是静态同步函数?
    方法上加上static关键字,并且使用synchronized关键字修饰 。
    静态同步函数使用的锁就不是this锁,因为this锁是属于对象级别的,静态同步函数应该使用类锁,即该类的字节码文件。
    可以使用getClass方法获取,也可以使用当前线程类的类名.class 获取该线程类的字节码文件。
    package chauncy.threadtrain;
    
    class ThreadTrain4 implements Runnable {
    	// 火车票总数
    	private static int count = 100;
    	//两个线程一定要用同一把锁
    	private Object obj = new Object();
    	public boolean flag=true;
    
    	@Override
    	public void run() {
    		//线程1 flag为true 线程2 flag为false
    		if(flag){
    			while (count > 0) {
    				//入参为obj情况下线程不安全,为this情况线程安全,证明同步函数使用的是this锁。
    				synchronized(ThreadDemo4.class){
    					if (count > 0) {
    						try {
    							Thread.sleep(4);
    						} catch (InterruptedException e) {
    						}
    						// 100总数减去现有数量count加1为当前出售的第几张票
    						System.out.println(Thread.currentThread().getName() + "出售第" + (100 - count + 1) + "张票");
    						count--;
    					}
    				}
    			}
    		}else{
    			while (count > 0) {
    				show();
    			}
    		}
    	}
    
    	/**
    	 * static 如果修饰的方法,直接通过类名.方法名进行调用   当class文件也是字节码文件被加载时,才会被初始化。 static存放在方法区 永久区
    	 * this关键字表示当前对象的锁,static使用类.class的锁
    	 * @methodDesc: 功能描述()  
    	 * @author: ChauncyWang
    	 * @param:    
    	 * @returnType: void
    	 */
    	public static synchronized void show() {
    		if (count > 0) {
    			try {
    				Thread.sleep(4);
    			} catch (InterruptedException e) {
    			}
    			// 100总数减去现有数量count加1为当前出售的第几张票
    			System.out.println(Thread.currentThread().getName() + "出售第" + (100 - count + 1) + "张票");
    			count--;
    		}
    	}
    }
    
    /**
     * @classDesc: 功能描述(静态同步函数)
     * @author: ChauncyWang
     * @version: 1.0
     */
    public class ThreadDemo4 {
    	public static void main(String[] args) throws InterruptedException {
    		//线程类一定要用一个实例,因为要重现变量共享的问题
    		ThreadTrain4 threadTrain4 = new ThreadTrain4();
    		// 1.创建两个线程
    		Thread thread1 = new Thread(threadTrain4);
    		Thread thread2 = new Thread(threadTrain4);
    		thread1.start();
    		Thread.sleep(10);
    		threadTrain4.flag=false;
    		thread2.start();
    	}
    }
    

四、多线程死锁

  1. 死锁的类型:数据库死锁、线程死锁、行锁、表锁等。
  2. 什么是多线程死锁?
    在同步中嵌套同步,导致锁无法释放。
  3. 锁在什么时候释放?
    锁一般在代码执行完毕之后自动释放,让其它线程拿到锁执行。
  4. 怎么避免死锁?
    同步中尽量不要嵌套同步。
  5. 死锁的代码实现:
    package chauncy.threadtrain;
    
    class ThreadTrain5 implements Runnable {
    	// 火车票总数
    	private static int count = 100;
    	//两个线程一定要用同一把锁
    	private Object obj = new Object();
    	public boolean flag=true;
    
    	@Override
    	public void run() {
    		//线程1 flag为true 线程2 flag为false
    		if(flag){
    			while (count > 0) {
    				//锁一般是在代码执行完毕之后自动释放,让其它线程拿到锁执行
    				//如果flag为true 先拿到obj这把锁,再拿到this锁,才能执行代码
    				//如果flag为false 先拿到this这把锁,再拿到obj锁,才能执行代码
    				synchronized(obj){
    					show();
    				}
    			}
    		}else{
    			while (count > 0) {
    				show();
    			}
    		}
    	}
    
    
    	public synchronized void show() {
    		synchronized(obj){
    			if (count > 0) {
    				try {
    					Thread.sleep(4);
    				} catch (InterruptedException e) {
    				}
    				// 100总数减去现有数量count加1为当前出售的第几张票
    				System.out.println(Thread.currentThread().getName() + "出售第" + (100 - count + 1) + "张票");
    				count--;
    			}
    		}
    	}
    }
    
    /**
     * @classDesc: 功能描述(死锁实现)
     * @author: ChauncyWang
     * @version: 1.0
     */
    public class ThreadDemo5 {
    	public static void main(String[] args) throws InterruptedException {
    		//线程类一定要用一个实例,因为要重现变量共享的问题
    		ThreadTrain5 threadTrain5 = new ThreadTrain5();
    		// 1.创建两个线程
    		Thread thread1 = new Thread(threadTrain5);
    		Thread thread2 = new Thread(threadTrain5);
    		thread1.start();
    		Thread.sleep(40);
    		threadTrain5.flag=false;
    		thread2.start();
    	}
    }
    

五、线程间实现同步(线程安全)的问题总结

  1. 什么是多线程安全?
    多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。做读操作是不会发生数据冲突问题。
  2. 如何解决多线程之间线程安全问题?
    使用多线程之间同步或使用锁(lock)。
  3. 为什么使用线程同步或使用锁能解决线程安全问题呢?
    将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。被包裹的代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。
  4. 什么是多线程之间同步?
    当多个线程共享同一个资源,不会受到其他线程的干扰
  5. 什么是同步代码块?
    是将可能会发生线程安全问题的代码,给包括起来。只能让当前一个线程进行执行,被包裹的代码执行完成之后才能释放所,让后才能让其他线程进行执行。
  6. 同步代码块与同步函数区别?
    同步代码使用自定锁(明锁),同步函数使用this锁。
  7. 同步函数与静态同步函数区别(一个静态方法和一个非静态静态怎么实现同步?)?
    同步函数使用this锁,静态同步函数使用字节码文件,也就是类.class。
  8. 什么是多线程死锁?
    同步中嵌套同步,解决办法:同步中尽量不要嵌套同步。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值