线程的同步(并发)_锁定静态属性_单例设计模式_Double Checking(提高线程效率)_线程的死锁与解锁(生产者消费者模式)__任务的调度_day31

1.线程的同步(并发):

多个线程访问一个资源,那么我们就要是资源安全,方法是使线程同步;


我们使用这个关键字;

线程安全的每次只能进去一个对象,就是给他加了一个锁,这个锁只有一把钥匙,所以只能一个完成后一个在进去;

同步的时候可以选择同步块或者同步方法;(同步又叫做锁定)

package Synchronize;

/**
 * 
 * 线程的同步问题(线程安全不安全)
 * 
 * @author Wang
 *
 */

public class Syn {

	public static void main(String[] args) {
		Web web = new Web();// 创建实体对象

		Thread thr1 = new Thread(web, "路人甲");// 创建四个代理 他们来实现多线程的抢票
		Thread thr2 = new Thread(web, "黄牛");
		Thread thr3 = new Thread(web, "程序员");
		Thread thr4 = new Thread(web, "白领");

		thr1.start();
		thr2.start();
		thr3.start();
		thr4.start();
	}

}

class Web implements Runnable {// 线程不安全的

	private int ticketNum = 10;
	private boolean flag = true;

	@Override
	public void run() {
		while (flag) {
			test6();
			//test5();
		}
	}

	public void test1() { // 线程不安全的
		if (ticketNum <= 0) {
			flag = false;
			return;
		}
		
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "抢到了" + ticketNum--);
	}

	public synchronized void test2() { // 线程安全的  这里加了一个锁
		if (ticketNum <= 0) {
			flag = false;
			return;
		}
		
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + "抢到了" + ticketNum--);
	}
	
	
	//线程安全  锁定正确
	public void test3(){
		//a  b  c
		synchronized(this){  //锁定块  线程安全的
			if(ticketNum<=0){
				flag=false; //跳出循环
				return ;
			}
			try {
				Thread.sleep(500); //模拟 延时
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNum--);
		}
	}
	
	//锁定范围不正确 线程不安全
	public void test4(){
		//   c  1
		synchronized(this){
			//b
			if(ticketNum<=0){
				flag=false; //跳出循环
				return ;
			}
		}
		// b
		try {
			Thread.sleep(500); //模拟 延时
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNum--);
	}//a -->1
	
	
	//线程不安全  锁定资源不正确
	public void test5(){
		//a  b  c
		synchronized((Integer)ticketNum){
			if(ticketNum<=0){
				flag=false; //跳出循环
				return ;
			}
			try {
				Thread.sleep(500); //模拟 延时
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNum--);
		}
	}
	
	//锁定的范围有误
	public void test6(){
	   	
		if(ticketNum<=0){
			flag=false; //跳出循环
			return ;
		}
		 //a  b  c 	
		synchronized(this){
			try {
				Thread.sleep(500); //模拟 延时
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNum--);
		}
	}
}

2.锁定静态的属性,我们在这里也介绍一下单例设计模式(我们在多线程的创建的时候用到了静态代理设计模式)

/**
 * 单例设计模式
 * 确保一个类只有一个对象
 * 懒汉式  是指在声明静态属性的时候不创建对象    double checking(双重检查提高现成的效率)
 * 1、构造器私有化,避免外部直接创建对象
 * 2、声明一个私有的静态变量
 * 3、创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象
 */

synchronized(Jvm.class){//锁住他的字节吗   让他无法创建对象  或者说锁住他的模板  但是再次可以提高效率(double checking )

package Synchronize;
/**
 * 
 * 单例设计模式确保一个类只有一个对象
 * 
 * @author Wang
 *
 */

/**
 * 单例设计模式:确保一个类只有一个对象
 * @author Administrator
 *
 */
public class SynchroizeDemo02 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		JvmThread thread1 = new JvmThread(100);
		JvmThread thread2 = new JvmThread(500);
		thread1.start();
		thread2.start();
		
	}

}
class JvmThread extends Thread{
	private long time;
	public JvmThread() {
	}
	public JvmThread(long time) {
		this.time =time;
	}
	@Override
	public void run() {		
		System.out.println(Thread.currentThread().getName()+"-->创建:"+Jvm.getInstance1(time));
	}
}


/**
 * 单例设计模式
 * 确保一个类只有一个对象
 * 懒汉式  声明静态变量的时候没有创建对象    double checking  双重检查提高效率
 * 1、构造器私有化,避免外部直接创建对象
 * 2、声明一个私有的静态变量
 * 3、创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象
 */
class Jvm {
	//声明一个私有的静态变量
	private static Jvm instance =null;	
	//构造器私有化,避免外部直接创建对象
	private Jvm(){
		
	}
	
	
	//创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象
		public static Jvm getInstance(long time){  //线程不安全的创建对像的地址都不一样

					if(instance == null){
						try {
							Thread.sleep(time); //延时 ,放大错误
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						instance =new Jvm();
					}
		
		  return instance;
		}
		
		
		
		//创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象
		public static Jvm getInstance1(long time){  
			
		
				synchronized(Jvm.class){//锁住他的字节吗   让他无法创建对象  或者说锁住他的模板  但是再次可以提高效率
					if(instance == null){//存在对象也需要等待
						try {
							Thread.sleep(time); //延时 ,放大错误
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						instance =new Jvm();
					}
				}
		
		  return instance;
		}
		
		
	//创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象
	public static Jvm getInstance2(long time){
		// c d e  -->效率  提供 已经存在对象的访问效率
		if(null==instance){	//这里我们用double-checking 的方法来提高现成的效率  
			// a b
			synchronized(Jvm.class){
				if(null==instance ){
					try {
						Thread.sleep(time); //延时 ,放大错误
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					instance =new Jvm();
				}
			}
	  }//a
	  return instance;
	}
	
}

单例设计方法的懒汉与饿汉式

package Synchronize;

/**
 * 单例创建的方式
 * 1、懒汉式
 * 1)、构造器私有化
 * 2)、声明私有的静态属性
 * 3)、对外提供访问属性的静态方法,确保该对象存在
 * 
 * @author Administrator
 *
 */
 
public class Demo03 {
	private static Demo03 instance;
	private Demo03(){
		
	}
	public static Demo03 getInstance (){
		if(instance == null){ //提供效率
			synchronized(Demo03.class){
				if(null==instance){ //安全
					instance =new Demo03();
				}
			}
		}
		return instance;
	}
	

}

/**
 * 饿汉式
   1)、构造器私有化 
 * 2)、声明私有的静态属性,同时创建该对象
 * 3)、对外提供访问属性的静态方法
 * @author Administrator
 *
 */
 
class MyJvm2 {
	private static MyJvm2 instance =new MyJvm2();
	private MyJvm2(){
		
	}
	public static MyJvm2 getInstance (){//饿汉式是不需要同步的也不需要判断对象是否存在因为  类会在时候的加载他的属性  那么就会在加载第一句的时候创建对象		
		return instance;
	}

}
/**
 * 类在使用的时候加载 ,延缓加载时间
 */
class MyJvm3 {
	private static class JVMholder{//内部类  这里就会提高效率  只有getInstance ()被调用的时候他才会被加载  跟MyJvm03加载就没什么关系了
		private static MyJvm3 instance =new MyJvm3();
	}
	private MyJvm3(){
		
	}
	public static MyJvm3 getInstance (){		
		return JVMholder.instance;
	}

}

注意:懒汉式与饿汉式都有提高效率的方法

3.过多的同步方法可能会造成死锁;

package Synchronize;

/**
 * 过多的同步方法可能造成死锁
 * @author Wang
 *
 */
public class Demo04 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Object g =new Object();
		Object m = new Object();
		Test t1 =new Test(g,m);
		Test2 t2 = new Test2(g,m);
		Thread proxy = new Thread(t1);
		Thread proxy2 = new Thread(t2);
		proxy.start();
		proxy2.start();
	}

}
class Test implements Runnable{
	Object goods ;
	Object money ;
	
	public Test(Object goods, Object money) {
		super();
		this.goods = goods;
		this.money = money;
	}

	@Override
	public void run() {
		while(true){
			test();
		}
	}
	
	public void test(){
		synchronized(goods){
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized(money){
				
			}
			
		}
		System.out.println("一手给钱");
	}
	
	
	
	
	
	
	
}

class Test2  implements Runnable{
	Object goods ;
	Object money ;
	public Test2(Object goods, Object money) {
		super();
		this.goods = goods;
		this.money = money;
	}

	@Override
	public void run() {
		while(true){
			test();
		}
	}
	
	public void test(){
		synchronized(money){
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized(goods){
				
			}
			
		}
		System.out.println("一手给货");
	}
	
	
	
}

4.解决死锁的办法  生产者消费者模式:(注意这个不是设计模式)

设计模式:是将类于类的关系;


package lock;

/**
 * 一个场景,共同的资源 生产者消费者模式 信号灯法 wait() :等待,释放锁 sleep 不释放锁 notify()/notifyAll():唤醒 与
 * synchronized
 * 
 * @author Administrator
 *
 */
public class Moive {
	private String pic;
	// 信号灯
	// flag -->T 生产生产,消费者等待 ,生产完成后通知消费
	// flag -->F 消费者消费 生产者等待, 消费完成后通知生产
	private boolean flag = true;

	public synchronized void play(String pic) {
		if (!flag) { // 生产者等待
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// 开始生产
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("生产了:" + pic);
		// 生产完毕
		this.pic = pic;
		// 通知消费
		this.notify();
		// 生产者停下
		this.flag = false;
	}

	public synchronized void watch() {
		if (flag) { // 消费者等待
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// 开始消费
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("消费了" + pic);
		// 消费完毕
		// 通知生产
		this.notifyAll();
		// 消费停止
		this.flag = true;
	}
}
package lock;

public class Player implements Runnable {

	private Moive m;

	Player(Moive m) {
		this.m = m;
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			if (i % 2 == 0) {
				m.play("缝纫机乐队");
			} else {
				m.play("最好的我们");
			}
		}
	}

}
package lock;

public class Watcher implements Runnable {
	private Moive m;

	Watcher(Moive m) {
		this.m = m;
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			m.watch();
		}

	}

}

package lock;

public class Apply {
	
	public static void main(String[] args) {
		
		Moive m = new Moive();
		
		Player p = new Player(m);
		Watcher w = new Watcher(m);
		
		new Thread(p).start();
		new Thread(w).start();
		
		
		
	}
}

5.任务的调度

package task;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
 *  
 * Timer() 
 * schedule(TimerTask task, Date time) 
 * schedule(TimerTask task, Date firstTime, long period) 
 * 可以自学一下  quartz(框架)  关于任务调度的
 * 
 * @author Wang
 *
 */

public class Time {

	public static void main(String[] args) {
		Timer timer =new Timer();
		timer.schedule(new TimerTask(){  //使用匿名内部类  创建一个对象  因为继承了Runnable所以还要重写run() 方法

			@Override
		public void run() {
			System.out.println("so easy....");
			}}, new Date(System.currentTimeMillis()+1000), 2000);//1s之后开始执行  每间隔两秒执行一次
	}

}


注意:

 wait() :等待,释放锁; sleep 不释放锁; notify()/notifyAll(): 唤醒 他们都必须要与 synchronized连用才有效果;

线程安全的效率就会低;


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值