多线程入门二

你猜 阅读:590 2021-03-31 16:50:33 评论:0

 

第一:线程安全问题

产生原因:多个线程同时共享,同一个全局变量或静态变量

实际案例:多线程模拟售票(线程不安全版本)

package com.zzg.concurrent; 
 
/** 
 * 模拟火车售票--展示线程不安全 
 *  
 * @author Administrator 
 * 
 */ 
public class Two implements Runnable { 
	private Integer ticket = 100; 
 
	@Override 
	public void run() { 
		// TODO Auto-generated method stub 
		while(ticket > 0) { 
			// 售票方法调用 
			sale(); 
		} 
	} 
 
	public void sale() { 
		if (ticket > 0) { 
			try { 
				Thread.sleep(10); 
			} catch (Exception e) { 
 
			} 
 
			System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - ticket + 1) + "张票."); 
			ticket--; 
		} 
	} 
 
} 
package com.zzg.concurrent; 
 
public class TwoDomain { 
 
	public static void main(String[] args) { 
		// TODO Auto-generated method stub 
		Two two = new Two(); 
		// 共享线程two 中ticket 属性 
		Thread thread1 = new Thread(two); 
		Thread thread2 = new Thread(two); 
		thread1.start(); 
		thread2.start(); 
		 
	} 
 
} 

效果:

Thread-1,出售 第1张票.
Thread-0,出售 第1张票.
Thread-0,出售 第3张票.
Thread-1,出售 第3张票.
Thread-0,出售 第5张票.
Thread-1,出售 第5张票.
Thread-1,出售 第7张票.

......

第二:线程安全解决办法

1问:如何解决多线程之间线程安全问题?

答:使用多线程之间同步(同步代码块、同步方法)或使用锁(lock)。

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

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

3问:什么是多线程之间同步?

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

 

1、同步代码块:将可能会发生线程安全问题的代码,给包括起来。

       语法格式:

                 synchronized(锁对象){

                          可能会发生线程冲突问题

                  }

/** 
 * 模拟火车售票--展示线程安全(同步块) 
 *  
 * @author Administrator 
 * 
 */ 
public class TwoBlock implements Runnable { 
	 
	private Integer ticket = 100; 
    // 自定义锁对象 
	private Object locker = new Object(); 
 
	@Override 
	public void run() { 
		// TODO Auto-generated method stub 
		while(ticket > 0) { 
			// 售票方法调用 
			sale(); 
		} 
	} 
 
	public void sale() { 
		synchronized(locker) { 
			if (ticket > 0) { 
				try { 
					Thread.sleep(10); 
				} catch (Exception e) { 
					 
				} 
				 
				System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - ticket + 1) + "张票."); 
				ticket--; 
			} 
		} 
	} 
 
}

执行函数:

package com.zzg.concurrent; 
 
public class TwoDomain { 
 
	public static void main(String[] args) { 
		// TODO Auto-generated method stub 
		//Two two = new Two();  
		TwoBlock two = new TwoBlock(); 
		// 共享线程two 中ticket 属性 
		Thread thread1 = new Thread(two); 
		Thread thread2 = new Thread(two); 
		thread1.start(); 
		thread2.start(); 
		 
	} 
 
} 

 效果展示:

Thread-0,出售 第1张票.
Thread-0,出售 第2张票.
Thread-0,出售 第3张票.
Thread-0,出售 第4张票.
Thread-0,出售 第5张票.
Thread-0,出售 第6张票.
Thread-0,出售 第7张票.
Thread-0,出售 第8张票.
Thread-0,出售 第9张票.
Thread-0,出售 第10张票.
Thread-0,出售 第11张票.
Thread-0,出售 第12张票.
Thread-0,出售 第13张票.
Thread-0,出售 第14张票.
Thread-0,出售 第15张票.
Thread-0,出售 第16张票.
Thread-0,出售 第17张票.
Thread-0,出售 第18张票.
Thread-0,出售 第19张票.
Thread-0,出售 第20张票.

...........

2、同步函数:在方法上修饰synchronized 称为同步函数

 语法格式:

                public void  synchronized methodName(paramter){

                          // 执行同步函数方法题

                }

package com.zzg.concurrent; 
/** 
 * 模拟火车售票--展示线程安全(同步函数) 
 *  
 * @author Administrator 
 * 
 */ 
 
public class TwoFunc implements Runnable { 
	private Integer ticket = 100; 
 
	@Override 
	public void run() { 
		// TODO Auto-generated method stub 
		while(ticket > 0) { 
			// 售票方法调用 
			sale(); 
		} 
	} 
 
	public synchronized  void sale() { 
		if (ticket > 0) { 
			try { 
				Thread.sleep(10); 
			} catch (Exception e) { 
 
			} 
 
			System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - ticket + 1) + "张票."); 
			ticket--; 
		} 
	} 
	 
	public static void main(String[] args) { 
		TwoFunc two = new TwoFunc(); 
		// 共享线程two 中ticket 属性 
		Thread thread1 = new Thread(two); 
		Thread thread2 = new Thread(two); 
		thread1.start(); 
		thread2.start(); 
	} 
	 
} 

效果展示:

Thread-0,出售 第1张票.
Thread-0,出售 第2张票.
Thread-0,出售 第3张票.
Thread-0,出售 第4张票.
Thread-0,出售 第5张票.
Thread-0,出售 第6张票.
Thread-0,出售 第7张票.
Thread-0,出售 第8张票.
Thread-0,出售 第9张票.
Thread-0,出售 第10张票.
Thread-0,出售 第11张票.
Thread-0,出售 第12张票.
Thread-0,出售 第13张票.
Thread-0,出售 第14张票.
Thread-0,出售 第15张票.
Thread-0,出售 第16张票.
Thread-0,出售 第17张票.
Thread-0,出售 第18张票.
Thread-0,出售 第19张票.
Thread-0,出售 第20张票.

...........

同步代码块与同步函数之间的锁区别:

同步代码块锁:自定义锁对象

同步函数锁:this 锁对象

 

3、静态同步函数

特点如下:

方法上加上static关键字,使用synchronized 关键字修饰 或者使用类.class文件。

静态的同步函数使用的锁是  该函数所属字节码文件对象

package com.zzg.concurrent; 
 
/** 
 * 模拟火车售票--展示线程安全(静态同步函数) 
 *  
 * @author Administrator 
 * 
 */ 
public class TwoStatic implements Runnable { 
	private static Integer ticket = 100; 
 
	@Override 
	public void run() { 
		// TODO Auto-generated method stub 
		while (ticket > 0) { 
			// 售票方法调用 
			sale(); 
		} 
	} 
 
	 
	public static void sale() { 
		synchronized (TwoStatic.class) { 
			if (ticket > 0) { 
				try { 
					Thread.sleep(10); 
				} catch (Exception e) { 
 
				} 
 
				System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - ticket + 1) + "张票."); 
				ticket--; 
			} 
		} 
	} 
	 
	public static void main(String[] args) { 
		TwoStatic two = new TwoStatic(); 
		// 共享线程two 中ticket 属性 
		Thread thread1 = new Thread(two); 
		Thread thread2 = new Thread(two); 
		thread1.start(); 
		thread2.start(); 
	} 
	 
 
} 

效果展示: 

Thread-0,出售 第1张票.
Thread-0,出售 第2张票.
Thread-0,出售 第3张票.
Thread-0,出售 第4张票.
Thread-0,出售 第5张票.
Thread-0,出售 第6张票.
Thread-0,出售 第7张票.
Thread-0,出售 第8张票.
Thread-0,出售 第9张票.
Thread-0,出售 第10张票.
Thread-0,出售 第11张票.
Thread-0,出售 第12张票.
Thread-0,出售 第13张票.
Thread-0,出售 第14张票.
Thread-0,出售 第15张票.
Thread-0,出售 第16张票.
Thread-0,出售 第17张票.
Thread-0,出售 第18张票.
Thread-0,出售 第19张票.
Thread-0,出售 第20张票.

...........

 

synchronized  总结:

synchronized 修饰方法使用锁是当前this锁。

synchronized 修饰静态方法使用锁是当前类的字节码文件

声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号