线程知识点总结二只要是生产者与消费之案例还有 Condition接口线程池

线程

案例:生产者和消费者(线程通信)(一)

在这里插入图片描述

public class Resource {
   //定义布尔类型成员   ,标志位,只是线程该做什么
	//false 没有   需要生产  true  需要消费
	boolean flag=false;
	
    int count;//包子计数器
    
}
public class Product  implements Runnable{
  //创建资源对象
	Resource r=new Resource();
	public void run() {
		while(true) {
		//对象资源判断
		if (r.flag==true) {
			//没有被消费,不能生产,等待
			try {wait();} catch (InterruptedException e) {e.printStackTrace();}
		}
		//可以生产
		r.count++;
		System.out.println("生产了第"+r.count+"个包子");
		r.flag=true;//可以消费
		//唤醒对方线程
		notify();
	    }
	}
}
public class Customer implements  Runnable {
	//创建资源对象
	Resource r=new Resource();
	
   public void run() {
	   while(true) {
		   //判断标志位
		   if (r.flag==false) {
			//需要生产,不能消费等待
			   try {wait();} catch (InterruptedException e) {e.printStackTrace();}
			   
		}
		   //可以消费了
		   System.out.println("消费了第"+r.count+"个");
		   //修改标志位,已经消费了 可以生产了
		   r.flag=true;
		   //唤醒生产线程
		   notify();
	   }
	   
   }
}
public class ThreadDemo {

	public static void main(String[] args) {
		
		//创建对象
		Runnable p=new Product();
		Runnable c=new Customer();
		Thread t0=new Thread(p);
		Thread t1=new Thread(c);
		t0.start();
		t1.start();
		
	}

}

以上程序启动线程后,抛出异常java.lang.IllegalMonitorStateException(运行时异常)异常称为无效的监视器状态异常.使用了线程的方法,来自于Object类:wait()notify(),必须出现在同步中

解决异常问题,使用同步代码块

实现消费者和生产者案例(二)

添加同步代码块的时候 ,程序停止不动

原因:notify()方法的时候,没有唤醒另一个线程,导致的生产的和消费的两个线程都处于wait()状态,两个方向的线程使用的锁不是一个

解决问题:两者使用同一个锁

public class Resource {
   //定义布尔类型成员   ,标志位,只是线程该做什么
	//false 没有   需要生产  true  需要消费
	boolean flag=false;
	
    int count;//包子计数器
    
}
public class Product  implements Runnable{
  //创建资源对象
	Resource r;
	public Product( Resource r) {
		this.r=r;
	}
	public void run() {
		synchronized(r) {
		while(true) {
		//对象资源判断
		if (r.flag==true) {
			//没有被消费,不能生产,等待
			try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}
		}
		//可以生产
		r.count++;
		System.out.println("生产了第"+r.count+"个包子");
		r.flag=true;//可以消费
		//唤醒对方线程
		r.notify();
	    }
	}
}
}
public class Customer implements  Runnable {
	//创建资源对象
	Resource r;
	public Customer( Resource r) {
		this.r=r;
	}
	
   public void run() {
	   while(true) {
		   //判断标志位
		   synchronized(r) {
		   if (r.flag==false) {
			//需要生产,不能消费等待
			   try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}
			   
		}
		   //可以消费了
		   System.out.println("消费了第"+r.count+"个");
		   //修改标志位,已经消费了 可以生产了
		   r.flag=false;
		   //唤醒生产线程
		  r.notify();
	    }
	   }
   }
}
public static void main(String[] args) {
		Resource r=new Resource();
		//创建对象
		Runnable p=new Product(r);
		Runnable c=new Customer(r);
		Thread t0=new Thread(p);
		Thread t1=new Thread(c);
		t0.start();
		t1.start();
		
	}


线程的方法问题

Object类的方法wait(),Threaad类的方法sleep()

  • 问题 :为什么等待唤醒的方法,定义在Object类中,而不是Thread
    • 同步锁导致的,任何对象都能作为所锁,保证任何一个锁对象都能调用等待和唤醒
  • wait()和sleep()方法的区别
    • wait()只能出现在同步中,必须是锁独享调用
    • sleep()方法可以随时使用,不依赖同步
    • wait()方法释放同步锁,被唤醒,重写获取锁
    • sleep()方法不释放锁

实现消费者和生产者案例(三)

私有修饰成员变量,提供方法访问

public class Resource {
   //定义布尔类型成员   ,标志位,只是线程该做什么
	//false 没有   需要生产  true  需要消费
	 private boolean flag=false;
	
     private int count;//包子计数器
    //提供方法,生产
     public synchronized void proruct() {
    	 //判断变量=true;不能生产,等待
    	 if (flag=true) {
			try {
				this.wait();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
    	 count++;
    	 System.out.println("生产了第"+count+"个");
    	 //修改标志类
    	 flag=true;
    	 //唤醒消费线程
    	 this.notify();
     }
     //提供方法消费
     public synchronized void customer() {
    	 //判断变量=false,不能消费,等待
    	 if (flag==false) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
    	 System.out.println("消费了第"+count+"个");
    	 //修改标志位
    	 flag=false;
    	 //唤醒生产线程
    	 this.notify();
     }
}
public class Product  implements Runnable{
  //创建资源对象
	 private Resource r;
	public Product( Resource r) {
		this.r=r;
	}
	public void run() {
        while(true){
		r.proruct();
        }
}
}
public class Customer implements  Runnable {
	//创建资源对象
	private  Resource r;
	public Customer( Resource r) {
		this.r=r;
	}
	
   public void run() {
	   while(true) {
		  r.customer();;
   }
 }
}
public class ThreadDemo {

	public static void main(String[] args) {
		Resource r=new Resource();
		//创建对象
		Runnable p=new Product(r);
		Runnable c=new Customer(r);
		Thread t0=new Thread(p);
		Thread t1=new Thread(c);
		t0.start();
		t1.start();
		
	}

}

多生产者和多消费者

notify()方法唤醒的时候往往是最先等待的

我们需要什么效果 :生产线程唤醒消费线程,消费线程唤醒生产线程,现在唤醒的可能是本方线程(NO)

但是,线程是一个独立的方法栈,CPU去里面数据运行,线程之间不认识谁是本方,谁是对方

使用Object类的方法notifyAll()唤醒所有的线程

线程全部唤醒后,依然没有达到需要的结果,因为线程唤醒后直接就运行了,不会判断flag标志是什么,线程已经判断过了

线程全部唤醒后也不能立即执行,再次判断flag,允许生产再生产,如果不允许,继续等

public class Resource {
   //定义布尔类型成员   ,标志位,只是线程该做什么
	//false 没有   需要生产  true  需要消费
	 private boolean flag=false;
	
     private int count;//包子计数器
    //提供方法,生产

public synchronized void proruct() {
    	 //判断变量=true;不能生产,等待
    	 while (flag==true) {
			try {
				this.wait();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
    	 count++;
    	 System.out.println("生产了第"+count+"个......");
    	 //修改标志类
    	 flag=true;
    	 //唤醒消费线程
    	 this.notifyAll();
     }
     //提供方法消费
     public synchronized void customer() {
    	 //判断变量=false,不能消费,等待
    	 while (flag==false) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
    	 System.out.println("消费了第"+count+"个");
    	 //修改标志位
    	 flag=false;
    	 //唤醒生产线程
    	 this.notifyAll();
     }
}

多生产者与消费者问题分析

  • notifyAll()唤醒的全部的线程.资源浪费
    • 能不能只唤醒对方一个
  • wait(,notifyo.notifyAll()都是本地方法,C++编写方法,和操作系统交互
    • 通知操作系统,将线程挂起不要允许,通知操作系统,让线程继续允许
    • 操作系统找CPU来实现线程的等待和唤醒
    • 频繁的和操作系统交互,降低了程序的效率

多生产者和多消费者(线程通信)改进

JDK1.5出现接口Lock,方法lock(),unlock()

接口出现目的取代synchronized

Lock接口

接口中定义方法:

  • condition newcondition()返回一个Condition的接口类型
    • Condition接口取代object类的监视器方法
Condition接口

作用︰线程的阻塞队列,本身是一个容器,存储的是线程.
进入到队列的线程,释放同步锁
一个锁Lock接口对象,可以产生出多个线程的阻塞队列.让线程释放同步锁后,进入到队列
需要唤醒线程的时候,指定唤醒哪一个队列中的线程

好处:不会全部唤醒,不会和操作系统进行交互,提高效率

Condition接口方法:

  • void await()线程释放锁,并进去到线程的阻塞队列,取代了Object类的方法wait()
  • void singal()线程从阻塞队列出来,获取锁再运行,取代了Object类的方法notify()
/*
 * 多生产和多消费的案例改进
 * Lock接口为锁使用
 * Condition接口,做为线程的阻塞队列体改效率节约资源
 */
public class Resource {
   //定义布尔类型成员   ,标志位,只是线程该做什么
	//false 没有   需要生产  true  需要消费
	 private boolean flag=false;
	
     private int count;//包子计数器
     //创建Lock接口的实现类对象,作为锁,去掉synchronized
     private Lock lock=new ReentrantLock();
     //通过这个锁对象,产生两个存储线程的容器(阻塞队列)
     //lock接口方法newCondition
     Condition pc=lock.newCondition();//线程的阻塞队列 ,生产队列
     Condition cc=lock.newCondition();//线程的阻塞队列 ,消费队列
     
     
    //提供方法,生产
      public   void proruct() {
    	  //获取锁
    	  lock.lock();
    	 //判断变量=true;不能生产,等待
    	 while (flag==true) {
			//线程释放锁,道阻塞队列中待着
    		 try {pc.await();} catch (InterruptedException e) {e.printStackTrace();
			}}
    	 count++;
    	 System.out.println("生产了第"+count+"个......");
    	 //修改标志类
    	 flag=true;
    	 //唤醒对方的线程
    	 cc.signal();
    	 //释放锁
    	 lock.unlock();
    	
     }
     //提供方法消费
     public void customer() {
    	 //获取锁
    	 lock.lock();
    	 //判断变量=false,不能消费,等待
    	 while (flag==false) {
			//线程等待  ,释放锁,进入队列
    		 try {cc.await();} catch (InterruptedException e) {e.printStackTrace();}
		}
    	 System.out.println("消费了第"+count+"个");
    	 //修改标志位
    	 flag=false;
    	 //唤醒生产线程
    	 pc.signal();
    	lock.unlock();
     }
}

线程池

概念:缓冲池是为了提高效率使用的,线程池也是缓冲池的一种(连接池,对象池)

为什么要出现线程池功能,创建线程是要和操作系统进行交互,线程运行完毕(run()结束)频繁创建线程,大量的浪费操作系统的资源,为了解决资源销毁和体改效率问题,人们设计出了线程池

线程池的思想,创建一些线程,存储在一个容器中,不要让线程销毁,需要的时候拿一个线程出来,线程执行完任务的时候,放回到容器中,存储线程的容器,线程池

JDK内置线程池

java.util.concurrent.Executors工厂类,创建对象,创建线程池对象

创建线程对象的方法:

Executorservice newFixedThreadPoo1(int n)创建具有固定个数的线程池,返回的是接口类型,实现类就是创建的线程对象

java.util.ExecutorService接口的方法submit( Runnable r)提交线程执行的任务

java.util.concurrent.callable接口,视为Runnable接口的扩展

接口的抽象方法:call()具有返回值的方法,而且还能抛出异常,此方法作为线程的任务使用,可以取到方法的返回值

线程池提交线程任务方法submit(Callable c)传递接口实现类,提交任务的方法submit有返回值
返回的是Future的接口类型,接口的实现类,就是线程运行的结果返回值

public class MyCallable implements Callable<String> {
   public  String call()throws Exception {
	   return "执行结果";
   }
}
public class ThreadDemo {
   public static void main(String[] args)throws Exception {
	ExecutorService es=Executors.newFixedThreadPool(2);
	//提交线程任务submit,接收submit的方法返回值接口类型
	    Future<String>f=es.submit(new MyCallable());
	System.out.println(f.get());
}
}
public class ThreadDemo {
public static void main(String[] args)throws Exception {
	//创建两个固定数的线程
	ExecutorService es=Executors.newFixedThreadPool(2);
	//提交任务
	Future<Integer>f= es.submit(new MyCallable(100));
	System.out.println(f.get());
	Future<Integer>f1= es.submit(new MyCallable(200));
	System.out.println(f1.get());
}
}
public class MyCallable implements Callable<Integer>{
	private int a;
	public MyCallable(int a) {
		this.a=a;
	}
   public Integer call() {
	   int sum=0;
	   for (int i = 0; i <=a; i++) {
		sum=sum+i;
			
	}
	   return sum;
   }
}

ConcurrentHashMap

java.uti1.concurrent.ConcurrentHashMap实现Map接口,是键值对的集合

集合特点:

  • 底层哈希表结构
  • 保证键的唯一性,键对象重写hashCode和equals
  • 是线程安全的集合,半安全
    • 不更改集合中的元素,不会锁定(synchronized)
    • 改变的元素,使用同步锁
    • 操作的是哪个数组的索引,锁定当前数组索引
  • 不会出现并发修改异常
  • 不能存储null值,null键
public class ThreadDemo {
public static void main(String[] args) {
	 //HashMap<String, String> map=new HashMap<String, String>();
	 ConcurrentHashMap<String, String> map=new ConcurrentHashMap<String, String>();
       map.put("a", "1");
       map.put("b", "2");
      map.put("c", "3");
   Set<String> set=map.keySet();
  Iterator<String> it=set.iterator();
   while(it.hasNext()) {
	   String key=it.next();
	   map.put("d", "4");
	   System.out.println(key+map.get(key));
	   
   }

}
}
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		// HashMap<Integer, Integer> map=new HashMap<Integer, Integer>();
		ConcurrentHashMap<Integer, Integer> map=new ConcurrentHashMap<Integer, Integer>();
	//集合储存2000个元素
		 for (int i = 1; i <=2000; i++) {
			map.put(i, 0);
		}
		 new Thread(new Runnable() {
			
			@Override
			public void run() {
				for (int i = 1; i <=500; i++) {
					map.remove(i);
				}
				
			}
		}).start();
	//线程,删除Map集合501-1000
		 new Thread(new Runnable() {
			
			@Override
			public void run() {
				for (int i = 501; i <=1000; i++) {
					map.remove(i);
				}
				
			}
		}).start();
	Thread.sleep(3000);
	System.out.println(map.size());
	}

}

原子操作

原子操作就是不可分割的操作

i++非原子操作,出现线程的安全问题

Atomiclnteger

整数的原子类,实现对整数的操作保证线程的安全

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值