java多线程:线程安全问题,synchronized关键字,同步锁,线程停止方式。

线程安全问题,synchronized,同步锁

package com.qianfeng.test;
/* 线程安全问题:
* 分析:4个线程共用了一个数据,出现了-1,-2,-3等错误的数据
* 
* 具体分析:1.共用了一个数据
* 2.共享语句有多条,一个线程使用cpu,没有使用完,cpu被抢走,当再次抢到cpu的时候,直接执行后面的语句,造成了错误的发生.
* 
* 解决:
* 在代码中使用同步代码块儿(同步锁)
* 解释:在某一段任务中,同一时间只允许一个线程执行任务,其他的线程即使抢到了cpu,也无法进入当前的任务区间,只有当当前的线程将任务执行完后,
* 其他的线程才能有资格进入
* 
* 同步代码块儿的构成:
* synchronized(锁(对象)){
* 	  同步的代码
* }
* 
* 对作为锁的对象的要求:1.必须是对象      2.必须保证被多个线程共享
* 可以充当锁的:1.一个普通的对象      2.当前对象的引用--this    3.类的字节码文件
* 
* 同步代码块儿的特点:1.可以保证线程的安全     2.由于每次都要进行判断处理,所以降低了执行效率
* 
* 总结:什么时候使用同步代码块儿
* 1.多个线程共享一个数据
* 2.至少有两个线程
*/
//第二种:线程与任务分离
public class Demo3 {
	
	public static void main(String[] args) {
		
		//创建任务对象
		Ticket1 ticket = new Ticket1();
		//创建线程对象并关联同一个任务
		//如果我们创建了自己独立的任务类,线程会优先调用我们手动传入线程的任务类对象的run方法,不会再去调用Thread默认的run方法
		Thread seller1 = new Thread(ticket);
		Thread seller2 = new Thread(ticket);
		Thread seller3 = new Thread(ticket);
		Thread seller4 = new Thread(ticket);
		
		//开启线程
		seller1.start();
		seller2.start();
		seller3.start();
		seller4.start();
	}
}

//创建任务类
class Ticket1 implements Runnable{
	
	//因为Ticket对象被四个线程共享,所以num作为属性也被共享了
 	int num = 20;
 	boolean flag = false;
 	//让object充当锁
 	//作为锁要满足两个条件:1.必须是对象   2.必须供所有的线程共享.
 	//可以作为锁的有:1.任意一个实例对象   2.this   3.字节码文件对象
 	Object object = new Object();
	public void run() {
		
		while (!flag) {
			synchronized (object) {//同步代码块儿--让线程之间互斥
				//制造一个延迟,相当于让当前执行run的线程休息一会儿(临时让出cpu)
				try {
					Thread.sleep(100);//100是时间,单位是毫秒
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				if (num >0) {
					System.out.println(Thread.currentThread().getName()+"  "+ --num);
				}else {
					flag = true;
				}
			}
		}
	}
}

synchronized关键字的理解

package com.qianfeng.test;
/*
 * 实例:两个人向同一个账户里面存钱
 * 一人存三次,每次存100
 * 
 * 
* 注意:1.当在一个类中同时存在多个synchronized修饰的代码块儿或函数时,要想安全,就必须让他们后面的对象一致。因为只有同一把锁才能安全。
 * 同步函数的锁:this
 * 2静态同步函数在进内存的时候不会创建对象,但是存在其所属类的字节码文件对象,属于class类型的对象,所以
 * 静态同步函数的锁是其所属类的字节码文件对象
 * 
 * 
 * * 理解synchronized关键字
 * 1、synchronized关键字的作用域有二种: 
1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象
的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的
一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。
这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时
访问相同类的另一个对象实例中的synchronized方法; 
2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问
这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,
表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/区块/},它的作用域是当前对象;

3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 
在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定
它的某个方法为synchronized方法;
	 
	 */
public class Demo4 {
	
	public static void main(String[] args) {
		
		//1.创建任务类对象
		CunQian cunQian = new CunQian();
		//2.创建线程并绑定任务
		Thread thread1 = new Thread(cunQian);
		Thread thread2 = new Thread(cunQian);
		//3.开启线程
		thread1.start();
		thread2.start();
	}
}

class Bank{
	
	int sum;//放的是当前账户的钱
	//使用同步代码块儿
//	public void addMoney(int money){
//		synchronized (this) {
//			sum+=money;
//			System.out.println(sum);
//	    }
//	}
	//使用同步函数
	//非静态的同步函数
	//相当于默认在synchronized后面跟着this充当锁
//	public synchronized void addMoney(int money){
//		sum+=money;
//		System.out.println(sum);
//	}
	
	//静态的同步函数
	//相当于默认在synchronized后面跟着当前的类的字节码文件充当锁----Bank.class
	public synchronized static void addMoney(int money){

	}
}

//创建任务类
class CunQian implements Runnable{
	
	Bank bank = new Bank();
	public void run() {
		for(int i=0;i<3;i++){
			bank.addMoney(100);
		}
	}
}

懒汉式单例中使用同步代码块

package com.qianfeng.test;
/*
 * 懒汉式单例中使用同步代码块
 */
public class Demo5 {

}

//懒汉式
class SingleInstance{

	private static SingleInstance singleInstance = null;
	public SingleInstance() {
		
	}
	
	//因为同步代码块的效率高于同步函数,所以尽量使用同步代码块
	public static SingleInstance getInstance(){
		if(singleInstance==null){//目的:尽量减少线程安全代码的判断次数,提高效率。
			synchronized(SingleInstance.class){
				if(singleInstance==null){
					singleInstance = new SingleInstance();
				}
			}
		}
		return singleInstance;
	}
}

//饿汉式
class SingleInstance1{

	private final static SingleInstance1 singleInstance = new SingleInstance1();
	
	public SingleInstance1() {
		
	}
	
	public static SingleInstance1 getInstance(){
		return singleInstance;
	}
}

class Test implements Runnable{

	@Override
	public void run() {
		SingleInstance1 singleInstance1 = SingleInstance1.getInstance();
	}
	
}

package com.qianfeng.test;

public class Demo6 {

	public static void main(String[] args) {
		
		Thread thread1 = new Thread();
		//1.可以,这里将thread1当作了任务类对象,执行的时候调用的是thread1内部的run方法
		Thread thread2 = new Thread(thread1);
		thread2.start();
		
		//2.创建Thread类的匿名子类对象充当线程类
		new Thread(){
			public void run(){
				System.out.println("hah");
			};
		}.start();
	}
}

线程停止的3种方式:标识,stop,interrupt()方法。wait()方法。

package com.qianfeng.test;
/*
 * 线程的停止:如何让它的任务结束
 * 1.通过一个标识去结束线程
 * 2.通过调用stop方法去结束线程--有固有的安全问题,已经过时,不建议再使用。
 * 3.调用interrupt()方法结束线程
 * 	原理:线程可以调用wait()方法,让当前的线程处于钝化的状态(会立刻释放cpu,并且处于无法抢cpu的状态,但是当前的线程并没有死亡)
 * 		注意点:wait方法必须在同步状态下使用。
 * 调用interrupt方法就是将wait状态的线程停止。
 */

//1.通过标识去结束进程
//public class Demo7 {
//
//	public static void main(String[] args) {
//		
//		Test1 test1 = new Test1();
//		Thread thread = new Thread(test1);
//		thread.start();
//		
//		//让主线程睡一会
//		try {
//			Thread.sleep(100);
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
//		
//		int i = 0;
//		while(true){
//			if (++i == 10) {
//				test1.flag = false;//当主线程执行到某个阶段的时候,让flag值变成false,控制while循环的结束,从而控制子线程的结束
//				break;//目的:让主线程结束
//			}
//		}
//	}
//}
//
//class Test1 implements Runnable{
//
//	boolean flag = true;
//	public void run() {
//		while(flag){
//			System.out.println(Thread.currentThread().getName()+" "+"我们很happy");
//		}
//	}
//}

//3.通过调用interrupt()方法结束线程
public class Demo7 {

	public static void main(String[] args) {
		
		Test1 test1 = new Test1();
		Thread thread = new Thread(test1);
		thread.start();
		
		//让主线程睡一会
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		int i = 0;
		while(true){
			if (++i == 10) {
				thread.interrupt();//当调用这个方法的时候,会触发wait方法的InterruptedException异常
									//我们就可以在捕获异常的时候将flag的值变成false,从而结束循环、任务、线程。
				break;//目的:让主线程结束
			}
		}
	}
}

class Test1 implements Runnable{

	boolean flag = true;
	public synchronized void run() {
		while(flag){
			try {
				this.wait();
			} catch (InterruptedException e) {
				flag = false;
				System.out.println("InterruptedException");
			}//wait方法由锁对象来调用
			System.out.println(Thread.currentThread().getName()+" "+"我们很happy");
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值