多线程2

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 Demo2 {
	public static void main(String[] args) {
		//1.先创建任务类对象
		Ticket ticket = new Ticket();
		//2.创建线程对象,并将任务交给线程
		//如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
		//所以说明任务类中的run方法优先级高于Thread内部的run()
		Thread thread1 = new Thread(ticket);
		Thread thread2 = new Thread(ticket);
		Thread thread3 = new Thread(ticket);
		Thread thread4 = new Thread(ticket);
		//3.开启线程
		thread1.start();
		thread2.start();
		thread3.start();
		thread4.start();
		
	}
}

//创建任务类
class Ticket implements Runnable{
	//所有的线程共享num
  int num = 20;
  boolean flag = false;
	/*
	 * 可以作为锁的条件:
	 * 1.必须是对象
	 * 2.锁必须被多个线程共享
	 * 
	 * 可以作为锁的:普通的对象,this,类的字节码文件(.class)
	 */
    Object object = new Object();
	public void run() {
		
	    while(!flag){
	    
	    	synchronized (object) {//同步代码块儿---实现了多个线程间的互斥
		    	//制造一个延迟,相当于让当前的线程睡一会儿(临时让出cpu),注意:线程在哪里睡的,就会停留在哪里.
		    	try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		    	
		    	if (num > 0) {
		    		System.out.println(Thread.currentThread().getName()+"   "+ --num);
				}else {
					flag = true;
				}
	    	}
	    }
	}
}
package com.qianfeng.test;
/*
 * 实例:两个人同时向银行账户存钱
 * 分析:两个人相当于两个线程
 * 操作的是一个数据
 * 
 * 比较同步代码块儿和同步函数
 * 同步代码块儿使用更加的灵活,这给需要同步的部分代码同步即可,而同步函数是给这个函数内的代码同步.
 * 由于处于同步的代码越少越好,所以最好使用同步代码块儿
 * 
 * * 注意: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 Demo3 {
	public static void main(String[] args) {
		//创建任务对象
		CunQian cunQian = new CunQian();
		//创建线程绑定任务
		Thread thread1 = new Thread(cunQian);
		Thread thread2 = new Thread(cunQian);
		//开始存钱
		thread1.start();
		thread2.start();
	}
}

class Bank{
	int sum;
	//使用同步代码块儿实现同步
//	public void  addMoney(int num) {
//		synchronized (this) {
//			sum+=num;
//			System.out.println(sum);
//		}
//	}
	//同步函数---将函数直接变成同步的
	//非静态的同步函数
	/*
	 * 默认在synchronized后面有锁,这把锁默认是this
	 */
	public synchronized   void  addMoney(int num) {
		sum+=num;
		System.out.println(sum);
	}
	
	//静态的同步函数
	/*
	 * 默认在synchronized后面有锁,这把锁默认是当前类的字节码文件
	 */
	public synchronized static void  addMoney1(int num) {
//		sum+=num;
//		System.out.println(sum);
	}
}

class CunQian implements Runnable{
	Bank bank = new Bank();
	public  void run() {
		for(int i=0;i<4;i++){
			bank.addMoney(100);
		}
	}
}
package com.qianfeng.test;

public class Demo4 {

}

//饿汉式
//由于共享的代码只有一行,不会发生线程安全
class SingleInstance{
	private final static SingleInstance singleInstance = new SingleInstance();
	private  SingleInstance() {
	}
	public static SingleInstance getInstance() {
		return singleInstance;
	}
}

//懒汉式
class SingleInstance1{
	private static SingleInstance1 singleInstance = null;
	private  SingleInstance1() {
	}
	public  static SingleInstance1 getInstance() {
		if (singleInstance == null) {//目的:尽量减少线程安全代码的判断次数,提高效率
			
			synchronized (SingleInstance1.class) {
				if (singleInstance == null) {
					singleInstance = new SingleInstance1();
				}
			}
			
		}
		return singleInstance;
	}
}

class Test implements Runnable{
	public void run() {
		SingleInstance1.getInstance();
	}
}

package com.qianfeng.test;
/*
 * 两个线程的通信
 * 
 * 实例:打印机打印----不断输入不断输出
 * 两个线程:输入线程和输出线程
 * 两个任务区:输入任务,输出任务
 * 一份数据
 */
public class Demo5 {
	public static void main(String[] args) {
		//1.准备数据
		Des des = new Des();
		//2.创建任务
		Input input = new Input(des);
		Output output = new Output(des);
		//3.创建线程
		Thread inputThread = new Thread(input);
		Thread outputThread = new Thread(output);
		//4.开启线程
		inputThread.start();
		outputThread.start();
	
	}
}

//将数据面向对象处理,形成数据类
class Des{
	String name;
	String sex;
}

//建立输入任务,输出任务
class Input implements Runnable{
	Des des;
	public Input(Des des) {
		super();
		this.des = des;
	}
	public  void run() {
		int i=0;
		while (true) {
			/*
			 * 需要给输入任务和输出任务同时加一把锁,保证两个任务之间是同步的
			 * 给两个任务加一把锁:可以是des或者Object.class
			 * 分析:
			 * 不建议使用Object.class:由于Object的使用范围太大,可能造成不必要的错误.
			 * 使用des最合适,因为他只被当前的两个任务共享.
			 * 
			 *注意:对于当前的情况只给一个线程加锁,无法实现两个线程的同步.
			 */
			synchronized (des) {
				if (i==1) {
					des.name = "特没谱";
					des.sex = "男";
				}else {
					des.name = "小晋三";
					des.sex = "女";
				}
				i=(i+1)%2;
			}
		}
	}
}

class Output implements Runnable{
	Des des;
	public Output(Des des) {
		super();
		this.des = des;
	}
	public void run() {
		while (true) {
			synchronized (des) {
			    System.out.println("姓名:"+des.name+"    性别:"+des.sex);
			}
		}
	}
}
package com.qianfeng.test;
/*
 * 两个线程的通信
 * 
 * 实例:打印机打印----一次输入一次输出
 * 两个线程:输入线程和输出线程
 * 两个任务区:输入任务,输出任务
 * 一份数据
 * 
 *使用唤醒等待机制---notify()/notifyAll()/wait()
 *wait():让当前的线程变成等待的状态,放入一个池子(线程池),失去了抢cpu的能力,.等待唤醒(锁相当于给当前的线程做了一个标记)
 *
 *notify():让当前的线程从等待状态唤醒,相当于从池子中取出线程.(唤醒的是同一把锁下的任意一个线程)
 *
 *notifyAll():唤醒的是同一把锁下的所有线程
 **/

public class Demo6 {
	public static void main(String[] args) {
		//1.准备数据
		Des1 des = new Des1();
		//2.创建任务
		Input1 input = new Input1(des);
		Output1 output = new Output1(des);
		//3.创建线程
		Thread inputThread = new Thread(input);
		Thread outputThread = new Thread(output);
		//4.开启线程
		inputThread.start();
		outputThread.start();
	
	}
}

//将数据面向对象处理,形成数据类
class Des1{
	String name;
	String sex;
	boolean flag;//控制唤醒和等待之间的切换
}

//建立输入任务,输出任务
class Input1 implements Runnable{
	Des1 des;
	public Input1(Des1 des) {
		super();
		this.des = des;
	}
	public  void run() {

		int i=0;
		while (true) {
			/*
			 * 需要给输入任务和输出任务同时加一把锁,保证两个任务之间是同步的
			 * 给两个任务加一把锁:可以是des或者Object.class
			 * 分析:
			 * 不建议使用Object.class:由于Object的使用范围太大,可能造成不必要的错误.
			 * 使用des最合适,因为他只被当前的两个任务共享.
			 * 
			 *注意:对于当前的情况只给一个线程加锁,无法实现两个线程的同步.
			 */
			
			synchronized (des) {
				if (des.flag == true) {//当flag值为true的时候,让当前的线程处于等待
					try {
						//当执行这行代码的时候,这里对应的是哪个线程,就操作的是哪个线程
						des.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
				
				if (i==1) {
					des.name = "特没谱";
					des.sex = "男";
				}else {
					des.name = "小晋三";
					des.sex = "女";
				}
				i=(i+1)%2;
				
				//更换flag的值
				des.flag = !des.flag;
				//唤醒输出线程,如果输出线程现在本身是活跃状态,即我们没有线程可唤醒,称为空唤醒,程序允许空唤醒.
				//原理:唤醒同一把锁下的任意一个线程.
				des.notify();
			}
		}
	}
}

class Output1 implements Runnable{
	Des1 des;
	public Output1(Des1 des) {
		super();
		this.des = des;
	}
	public void run() {
		while (true) {
			synchronized (des) {
				if (des.flag == false) {//当flag值为false的时候,让当前的线程处于等待
					try {
						des.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}//让输出线程等待
				}
			    System.out.println("姓名:"+des.name+"    性别:"+des.sex);
			    
			    des.flag = !des.flag;
			    //唤醒输入线程
			    des.notify();
			}
		}
	}
}
package com.qianfeng.test;
/*
 * 两个线程的通信
 * 
 * //优化
//面向对象的精髓:谁的活儿谁干,不是你的活儿不要干
//将数据准备的活儿从输入任务输出任务提出来,放入数据类
 * 
 * 实例:打印机打印----一次输入一次输出
 * 两个线程:输入线程和输出线程
 * 两个任务区:输入任务,输出任务
 * 一份数据
 * 
 *使用唤醒等待机制---notify()/notifyAll()/wait()
 *wait():让当前的线程变成等待的状态,放入一个池子(线程池),失去了抢cpu的能力,.等待唤醒(锁相当于给当前的线程做了一个标记)
 *
 *notify():让当前的线程从等待状态唤醒,相当于从池子中取出线程.(唤醒的是同一把锁下的任意一个线程)
 *
 *notifyAll():唤醒的是同一把锁下的所有线程
 **/

public class Demo7 {
	public static void main(String[] args) {
		//1.准备数据
		Des2 des = new Des2();
		//2.创建任务
		Input2 input = new Input2(des);
		Output2 output = new Output2(des);
		//3.创建线程
		Thread inputThread = new Thread(input);
		Thread outputThread = new Thread(output);
		//4.开启线程
		inputThread.start();
		outputThread.start();
	
	}
}

//将数据面向对象处理,形成数据类


class Des2{
	String name;
	String sex;
	boolean flag;//控制唤醒和等待之间的切换
	
	//给输入准备数据
	public synchronized void setData(String name,String sex){
		if (flag == true) {//当flag值为true的时候,让当前的线程处于等待
			try {
				//当执行这行代码的时候,这里对应的是哪个线程,就操作的是哪个线程
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name;
		this.sex = sex;
		//更换flag的值
		flag = !flag;
		//唤醒输出线程,如果输出线程现在本身是活跃状态,即我们没有线程可唤醒,称为空唤醒,程序允许空唤醒.
		//原理:唤醒同一把锁下的任意一个线程.
		notify();
	}
	//给输出准备数据
	public synchronized void getData() {
		if (flag == false) {//当flag值为false的时候,让当前的线程处于等待
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}//让输出线程等待
		}
	    System.out.println("姓名:"+name+"    性别:"+sex);
	    flag = !flag;
	    //唤醒输入线程
	    notify();
	}
}

//建立输入任务,输出任务
class Input2 implements Runnable{
	Des2 des;
	public Input2(Des2 des) {
		super();
		this.des = des;
	}
	public  void run() {

		int i=0;
		while (true) {
			/*
			 * 需要给输入任务和输出任务同时加一把锁,保证两个任务之间是同步的
			 * 给两个任务加一把锁:可以是des或者Object.class
			 * 分析:
			 * 不建议使用Object.class:由于Object的使用范围太大,可能造成不必要的错误.
			 * 使用des最合适,因为他只被当前的两个任务共享.
			 * 
			 *注意:对于当前的情况只给一个线程加锁,无法实现两个线程的同步.
			 */
			if (i==1) {
				des.setData("特没谱", "男");
			}else {
				des.setData("小晋三", "女");
			}
			i=(i+1)%2;
			
		}
	}
}

class Output2 implements Runnable{
	Des2 des;
	public Output2(Des2 des) {
		super();
		this.des = des;
	}
	public void run() {
		while (true) {
			des.getData();
		}
	}
}
package com.qianfeng.test;
/*
 * 生产者消费者:
 * 单生产者单消费者-----会
 * 多生产者多消费者-----了解
 * 
 * 先学习单生产者单消费者
 * 需要的线程:两个---一个生产线程一个消费线程
 * 需要的任务:两个---一个生产任务一个消费任务
 * 需要数据:一份---产品
 */
public class Demo8 {
	public static void main(String[] args) {
		//准备数据
		Product product = new Product();
		//准备任务
		Producer producer = new Producer(product);
		Consumer consumer = new Consumer(product);
		//准备线程
		Thread proThread = new Thread(producer);
		Thread conThread = new Thread(consumer);
		//开启线程
		proThread.start();
		conThread.start();	
	}
}

//创建产品
class Product{
	String name;//产品的名字
	double price;//产品的价格
	int count;//生产的产品数量
	
	//标识
	boolean flag = false;
	
	//准备生产
	public synchronized void setProduce(String name,double price){
		if (flag == true) {
			try {
				wait();//让生产线程等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		this.name = name;
		this.price = price;
		System.out.println(Thread.currentThread().getName()+"   生产了:"+this.name+"   产品的数量:"+this.count+"   价格:"+this.price);
		
		count++;
		flag = ! flag;
		notify();//唤醒消费线程
	}
	//准备消费
	public  synchronized void getConsume() {
		if (flag == false) {
			try {
				wait();//让消费线程等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"   消费了:"+this.name+"   产品的数量:"+this.count+"   价格:"+this.price);
		//唤醒生产线程
		flag = ! flag;
		notify();
	}
}
//创建生产任务
class Producer implements Runnable{
	Product product;
	public Producer(Product product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.setProduce("bingbing", 10);
		}
		
	}
}
//创建消费任务
class Consumer implements Runnable{
	Product product;
	public Consumer(Product product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.getConsume();
		}
	}
}
package com.qianfeng.test;
/*
 * 生产者消费者:
 * 单生产者单消费者-----会
 * 多生产者多消费者-----了解
 * 
 * 学习多生产者多消费者
 * 需要的线程:四个---两个生产线程两个消费线程
 * 需要的任务:两个---一个生产任务一个消费任务
 * 需要数据:一份---产品
 * 
 * 生产任务与消费任务共用一个数据--产品类
 * 
 * 要求:最终也要实现一次生产一次消费
 * 
 *错误描述:当有两个生产线程,两个消费线程同时存在的时候,有可能出现生产一次,消费多次或者生产多次消费一次的情况.
 *原因:当线程被重新唤醒之后,没有判断标记,直接执行了下面的代码
 *
 *解决办法:将标记处的if改成while
 *
 *问题描述:继续运行程序,会出现死锁的情况(4个线程同时处于等待状态)
 *原因:唤醒的是本方的线程,最后导致所有的线程都处于等待状态.
 *
 *解决办法:将notify改成notifyAll.保证将对方的线程唤醒
 *
 *死锁:出现的情况有两种
 *1.所有的线程处于等待状态
 *2.锁之间进行嵌套调用
 *
 */
public class Demo9 {
	public static void main(String[] args) {
		//准备数据
		Product1 product = new Product1();
		//准备任务
		Producer1 producer = new Producer1(product);
		Consumer1 consumer = new Consumer1(product);
		//准备线程
		Thread t0 = new Thread(producer);
		Thread t1 = new Thread(producer);
		Thread t2 = new Thread(consumer);
		Thread t3 = new Thread(consumer);
		//开启线程
		t0.start();
		t1.start();	
		t2.start();	
		t3.start();	
	}
}

//创建产品
class Product1{
	String name;//产品的名字
	double price;//产品的价格
	int count;//生产的产品数量
	
	//标识
	boolean flag = false;
	
	//准备生产
	public synchronized void setProduce(String name,double price){
		while (flag == true) {
			try {
				wait();//让生产线程等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		this.name = name;
		this.price = price;
		System.out.println(Thread.currentThread().getName()+"   生产了:"+this.name+"   产品的数量:"+this.count+"   价格:"+this.price);
		
		count++;
		flag = ! flag;
		//notify();//唤醒消费线程
		notifyAll();
	}
	//准备消费
	public  synchronized void getConsume() {
		while (flag == false) {
			try {
				wait();//让消费线程等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"   消费了:"+this.name+"   产品的数量:"+this.count+"   价格:"+this.price);
		//唤醒生产线程
		flag = ! flag;
		//notify();
		notifyAll();
	}
}
//创建生产任务
class Producer1 implements Runnable{
	Product1 product;
	public Producer1(Product1 product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.setProduce("bingbing", 10);
		}
		
	}
}
//创建消费任务
class Consumer1 implements Runnable{
	Product1 product;
	public Consumer1(Product1 product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.getConsume();
		}
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值