java基础-day17-线程创建(二).同步锁 synchronized. 单例设计模式

多线程创建(二) 实现Runnable接口

注:重写Runnable接口的 run() 方法

public class test1 {
	public static void main(String[] args) {
		//4、创建线程对象
		MyRunnable target = new MyRunnable();
		//能不能  把  target和Thread  绑定关系--利用了Thread类的构造方法
		Thread thread = new Thread(target);
		//5、启动线程
		thread.start();
		//模拟多线程程序
		Thread thread2 = new Thread(target);
		//5、启动线程
		thread2.start();
		thread.setName("1号线程");
		thread2.setName("2号线程");
	}
}
//1、自定义多线程类  implements Runnable
//2、实现了接口以后的实现类,有两种选择:作为一个抽象类+把所有抽象方法都重写
class MyRunnable implements Runnable{
	//3、把你自己的业务,都放在重写的run()
	@Override
	public void run() {
		for( int i  = 0 ; i < 10 ; i++ ) {
			//Thread.currentThread()是获取正在执行任务的Thread对象。
			System.out.println(i+"==="+Thread.currentThread().getName());
		}
	}
}

售票案例

需求:设计4个售票窗口,总计售票100张

方案1:继承Thread
public class test2 {
	public static void main(String[] args) {
		//3、创建线程对象
		MyTicket target = new MyTicket();
		//4、启动线程
		target.start();
//问题1:我们总共要卖100张票,但是现在卖了400张票,为什么?-- 因为每个对象初始化时都初始化了自己的ticket100张票
		//模拟多线程
		MyTicket target2 = new MyTicket();
		target2.start();
		MyTicket target3 = new MyTicket();
		target3.start();
		MyTicket target4 = new MyTicket();
		target4.start();
	}
}
//1、自定义多线程类
class MyTicket extends Thread {
//	int tickets = 100 ; //定义变量,记录票数  //成员变量 - 实例变量  ,这个资源会跟着对象存在,new几次就有几份
	//问题1的解决方案:把资源变成共享资源,不跟随对象产生,保证程序中只有这1份资源,只有100张票。
	static int tickets = 100 ; 
	
	//2、所有业务放入重写的run()
	@Override
	public void run() {
		while(true) {//一直卖票
			if( tickets>0 ) {
				try {
					//5、增加延迟性的考察。
					//问题2:程序出现了超卖的现象:0 -1 -2张票。
					//问题3:程序出现了重复卖的现象:一张票卖给多个人。
					Thread.sleep(10);//让程序休眠10ms
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println( getName()+"="+tickets-- );
			}else 
				break;//<=0时结束死循环
		}
	}
}
方案2:实现 Runnable 接口
public class test3 {
	public static void main(String[] args) {
		//3、创建线程对象
		MyTickets2 target = new MyTickets2();
		Thread t1 = new Thread(target);
		//4、启动线程
		t1.start();	
		//模拟多线程
		Thread t2 = new Thread(target);
		t2.start();
		Thread t3 = new Thread(target);
		t3.start();
		Thread t4 = new Thread(target);
		t4.start();
	}
}
//1、自定义多线程类
class MyTickets2 implements Runnable{
	int tickets  = 100 ; 
	//2、重写run(),把业务放进来
	@Override
	public void run() {
		while(true) {//一直卖100张票
			if( tickets > 0 ) {
				//多线程的延迟访问
				try {
					//问题1:出现了超卖现象:卖出了0  -1  -2张票
					//问题2:出现了重卖现象:同一张票卖给了多个人
					Thread.sleep(10);//睡10ms
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//Thread.currentThread().getName() 正在执行任务的线程名称
				System.out.println( Thread.currentThread().getName() +"="+ tickets-- );
			}else {
				break;
			}
		}
	}
}

1、多线程安全问题是如何出现的?常见情况是由于线程的随机性 + 访问延迟sleep()。
2、以后如何判断程序有没有线程安全问题?在多线程程序中 + 有共享数据 + 多条语句操作 共享数据。

同步锁 synchronized关键字

同步是:共享资源同一时刻只有一个线程在操作,其他线程在等待。
异步是:多个线程一起操作了共享数据
synchronized 可以修饰方法称为同步方法。也可以修饰代码块称为同步代码块。
注:使用同步锁,需考虑两件事情: 使用锁的位置 + 使用锁的对象
如果共享的资源是静态资源,synchronized 锁对象为类的字节码,synchronized(类名.class)

售票案例解决
public class test4 {
	public static void main(String[] args) {
		// 3、创建线程对象
		MyTickets2 target = new MyTickets2();
		Thread t1 = new Thread(target);
		// 4、启动线程
		t1.start();
		// 模拟多线程
		Thread t2 = new Thread(target);
		t2.start();
		Thread t3 = new Thread(target);
		t3.start();
		Thread t4 = new Thread(target);
		t4.start();
	}
}

// 1、自定义多线程类
class MyTickets2 implements Runnable {
	int tickets = 100;
	// Object obj = new Object();
	String s = "abc";
	// 2、重写run(),把业务放进来
	@Override
	// synchronized public void run() {//8、同步方法
	public void run() {
		while (true) {// 一直卖100张票
			// 5、 同步锁的位置:是从问题发生点开始,到结束为止。
			// 6、 同步锁的对象:如果是同步代码块,根本不关心锁对象是谁,只要是同一个对象就可以。
			// synchronized (new Object()) {//每个线程有自己的对象
			// synchronized (obj) {//多个线程使用了同一个对象obj,可以解决问题
			// 7、同步可以修饰方法,如果方法里的代码都有安全隐患,直接修饰成同步方法就可以了。
			synchronized (s) {// 多个线程使用了同一个对象s,也可以解决问题
				if (tickets > 0) {
					// 多线程的延迟访问
					try {
						// 问题1:出现了超卖现象:卖出了0 -1 -2张票
						// 问题2:出现了重卖现象:同一张票卖给了多个人
						Thread.sleep(10);// 睡10ms
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					// Thread.currentThread().getName() 正在执行任务的线程名称
					System.out.println(Thread.currentThread().getName() + "=" + tickets--);
				} else {
					break;
				}
			}
		}
	}
}

单例设计模式

一个类在内存中的对象只有一个

饿汉式
public class test5 {
	public static void main(String[] args) {
		//4、调用了MySingleton内部创建好的对象
		MySingleton m1 = MySingleton.getSingle();
		//5、全局真的只要一个对象吗?
		MySingleton m2 = MySingleton.getSingle();
		//==比较引用类型比较的是地址值
		System.out.println(m1 == m2);
	}
}
//自定义单例类
class MySingleton {
	//1、私有化构造方法,目的是不让外界随意new
	private MySingleton() {}
	//2、在类的内部 ,提供创建好的对象
	//static--因为静态只能调静态,所以想要被getSingle()调用,必须也是静态资源
	static private MySingleton single = new MySingleton();
	//3、提供公共的 ,访问方式,把single返回给外界调用位置
	static public MySingleton getSingle(){
		return single ; 
	}
}
懒汉式
public class test6{
	public static void main(String[] args) {
		MySingleton2 m = MySingleton2.getSingle();
		MySingleton2 m2 = MySingleton2.getSingle();
		System.out.println(m==m2);
	}
}
class MySingleton2 {
	// 1、私有化构造方法,目的是不让外界随意new
	private MySingleton2() {}
	// 2、在类的内部 ,提供创建好的对象
	// static--因为静态只能调静态,所以想要被getSingle()调用,必须也是静态资源
	static private MySingleton2 single; // 懒汉式
	// 3、提供公共的 ,访问方式,把single返回给外界调用位置
	synchronized static public MySingleton2 getSingle() {// 直接把方法同步了,默认使用的锁对象是this
		// synchronized (MySingleton2.class){//如果你要锁的共享资源是静态的,此时,锁对象必须是MySingleton2的字节码对象
		if (single == null) {
			single = new MySingleton2();
		}
		return single;
		// }
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值