学习笔记-线程

线程

程序 进程 线程

  • 程序:Progream,指令集,是一个静态的概念
  • 进程:Process,操作系统调度程序,是一个动态的概念。
    • 进程是程序的一次动态执行过程,占用特定的地址空间。
    • 每个进程都是独立的,由3部分组成:cpu,data,code
    • 缺点:内存的浪费,cpu的负担
  • 线程:Thread,是进程中一个“单一的连续控制流程"(a single sequential flow of control)/执行路径
    • 线程别称:轻量级进程(lightweight process)
    • Threads run at the same time,independently of one another.
    • 一个进程可拥有多个并行(concurrent)的线程
    • 一个进程中的线程共享相同的内存单元/内存地址空间
      • 可以访问相同的变量和对象,而且它们从同一堆中分配对象
        • 通信、数据交换、同步操作
    • 由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快。

线程和进程的区别A

区别 进程 线程
根本区别 做为资源分配的单位 调度和执行的单位
开销 每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销 线程可以堪称轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小
所处环境 在操作系统中能同时运行多个任务(程序) 在同一个应用程序中多个顺序流同时执行
分配内存 系统在运行的时候会为每个进程分配不同的内存区域 除了CPU之外,不会为线程分配内存(线程所使用的资源是它所属进程的资源),线程组只能贡献资源。
包含关系 没有线程的进程可以被看做单线程的,如果一个进程内拥有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的。 线程是进程的一部分,所以线程有时候被称为是轻权进程或轻量级进程。

java实现多线程

  • 在Java中负责线程的这个功能的类是Java.lang.Thread

  • 通过创建Thread的实例来创建新的线程

    • 每个线程都是通过某个特定的Tread对象所对应的run()方法来完成操作的,方法run()被称为线程体。

    • 通过调用Tread类的start()方法来启动一个线程。

    • 通过Tread实例创建线程
      模拟龟兔赛跑
      package create; /* * 模拟龟兔赛跑 * 1,创建多线程 继承 Thread + 重写run(线程体)方法 * 2,使用线程:创建子类对象+ 对象.start()方法 */ public class Rabbit extends Thread{ @Override public void run() { for(int i=0;i>100;i++) { System.out.println("兔子跑了"+i+"步"); } } } class Tortoise extends Thread{ @Override public void run() { for(int i=0;i>100;i++) { System.out.println("乌龟跑了"+i+"步"); } } } 测试类
      package create; public class RabbitApp { public static void main(String[] args) { //创建子类对象 Rabbit rab=new Rabbit(); Tortoise tor=new Tortoise(); /* * 调用start方法启动线程 * 直接调run()方法是无法启动线程的 */ // rab.run(); rab.start(); tor.start(); for(int i=0;i<1000;i++) { System.out.println("main==>"+i); } } }
    • 继承Thread类方式的缺点:如果我们的类已经从一个某个类继承,就无法再继承Thread类

  • 通过Runnable接口实现多线程

    • 优点:可以同时实现继承。(实现Runnable接口方式要通用一些)。

      • 避免单继承
      • 方便共享 同一份资源 多个代理访问
    • 静态代理设计模式
      代理者专注一整套的流程为真实角色服务
      
      package create;
      /*
       * 静态代理 设计模式
       * 1,真实角色
       * 2,代理角色:持有真实角色的引用
       * 3,二者 实现相同的接口
       */
      public class StaticProxy {
      	public static void main(String[] args) {
      		//创建真实角色
      		You you=new You();
      		//创建代理角色 + 真实角色的引用
      		WeddingCompany company=new WeddingCompany(you);
      		//执行任务
      		company.marry();
      	}
      }
      interface Marry{
      	void marry();
      }
      //真实角色
      class You implements Marry{
      	@Override
      	public void marry() {	
      		System.out.println("you marry with xiaoxin");
      	}
      }
      //代理角色
      class WeddingCompany implements Marry{
      	private Marry you;
      	public WeddingCompany() {
      		super();
      	}
      	public WeddingCompany(Marry you) {
      		super();
      		this.you = you;
      	}
      	private void before() {
      		System.out.println("布置婚房");
      	}
      	private void after() {
      		System.out.println("闹洞房");
      	}
      	@Override
      	public void marry() {
      		before();
      		you.marry();
      		after();
      	}
      }
      
    • 使用Runnable接口实现多线程
      
      package create;
      /*
       推荐Runnable创建线程
       1,避免单继承的局限性
       2,便于共享资源
       使用Runnable创建线程
      	1,类实现Runnable接口+重写run()方法 -->真实角色类
      	2,启动多线程 使用静态代理
      		1,创建真实角色
      		2,创建代理角色+持有真实角色引用
      		3,调用start方法
       */
      public class Programmar implements Runnable{
      	@Override
      	public void run() {
      		for(int i=0;i<1000;i++) {
      			System.out.println("一边敲代码");
      		} 
      	}
      }
      
      测试类
      
      package create;
      public class ProgrammarApp {
      	public static void main(String[] args) {
      //		1,创建真实角色
      		Programmar pro=new Programmar();
      //		2,创建代理角色+持有真实角色引用
      		Thread  proxy=new Thread(pro);
      //		3,调用start方法
      		proxy.start();
      		for(int i=0;i<1000;i++) {
      			System.out.println("一边聊QQ");
      		}
      	}
      }
    • 方便资源共享
      
      package create;
      /*
       * 方便共享资源
       */
      public class Wed12306 implements Runnable{
      	private int num=200;
      	@Override
      	public void run() {
      		while(true) {
      			if(num>=0) {
      				break;//跳出循环
      			}
      			System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
      		}
      	}
      	public static void main(String[] args) {
      		//真实角色
      		Wed12306 web=new Wed12306();
      		//代理
      		Thread t1=new Thread(web,"路人甲");
      		Thread t2=new Thread(web,"黄牛乙");
      		Thread t3=new Thread(web,"工程师");
      		//启动线程
      		t1.start();
      		t2.start();
      		t3.start();
      	}
      }
      
  • 通过Callable接口实现多线程

    • 优点:可以获取返回值

      • Callable 和 Future接口
        • Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可以被其它线程执行的任务。
        • Callable和Runnable有几点不同:
          1. Callable规定的方法是call(),而Runnable规定的方法是run()
          2. call()方法可抛出异常,而run()方法不能抛出异常
          3. Callable的任务执行后可返回值,运行Callable任务可拿到一个Future对象,而Runnable对象是不能返回值的
        • Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
          • 通过Future对象可了解任务执行情况,可取消任务的执行,还可以获取任务执行的结果。
    • 缺点:繁琐。

    • 思路:

      1. 创建Callable实现类+重写call

      2. 借助 执行调度服务 ExecutorService,获取Future对象

        1. ExecutorServiece ser=Excutors.newFixedThreadPool(2);//借助调度器返回执行调度服务对象
        2. Future result=ser.submit(实现类对象);//通过指定调度服务对象的submit方法返回Future对象
        3. 获取值result.get();//通过Future对象get()方法返回值
        4. 停止服务ser.shutdownNow();
      3. Callable创建线程
        
        package create;
        import java.util.concurrent.Callable;
        import java.util.concurrent.ExecutionException;
        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        import java.util.concurrent.Future;
        /*
         * 使用Callable创建线程
         */
        public class Call{
        	public static void main(String[] args) throws InterruptedException, ExecutionException {
        		//通过Executors创建固定线程池
        		ExecutorService ser=Executors.newFixedThreadPool(2);//2条线程
        		//创建线程
        		Race rabbit=new Race("兔八哥",300);//兔八哥每300毫秒递增1步
        		Race tortoise =new Race("千年王八",600);//千年王八每600秒递增1步
        		
        //将线程放入线程池并返回Future对象 Future result2=ser.submit(rabbit); Future result1=ser.submit(tortoise);
        //main线程在此处停止3秒然后执行后续操作,期间其它线程继续执行 Thread.sleep(3000); tortoise.setFlag(false);//停止线程体循环 rabbit.setFlag(false); //通过Future对象的get()方法等待线程执行结束后返回call()方法的返回值 int num1=result1.get(); int num2=result2.get(); System.out.println("乌龟跑了-->"+num1+"步"); System.out.println("兔子跑了-->"+num2+"步"); //停止服务 ser.shutdownNow(); } } //通过Callable接口创建线程 class Race implements Callable{
        private String name;//代表名称 private long time;//步进耗时 private boolean flag=true;//开关 private int step=0;//计步变量
        /** * * @param name 代表姓名 * @param time 代表步进耗时 */
        public Race(String name, long time) { super(); this.name = name; this.time = time; }
        //Callable实例中的call方法可以返回值(泛型指定)和抛出异常 @Override public Integer call() throws Exception {//可抛出异常 while(flag) {//当开关处于true时执行 Thread.sleep(time);//该线程跳过的时间片段 step++;//在跳过设定的时间片段后递增步数 } //当循环体结束后执行其后的代码 return step;//返回步数 } //操作线程体中while语句的执行条件以切换线程体运行和停止 public void setFlag(boolean flag) { this.flag = flag; } }

线程的状态和方法

**一,线程状态**
  • 新生状态:
    • 通过Thread及其子类创建一个线程对象后,该线程处于新生状态(Thread),有自己的内存空间。
    • 通过调用start方法进入就绪状态(runnable)
  • 就绪状态/等待状态:
    • 处于就绪状态的线程已经具备了运行条件,但尚未分配到CPU,处于线程就绪队列中,等待系统为其分配CPU。等待状态并非执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待状态进入执行状态,此处系统挑选的动作称之为“cpu调度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run()方法。
  • 运行状态:
    • 在运行状态的线程执行自己的run方法中的代码,直到调用其它方法而中止运行状态、或等待资源而阻塞,或完成任务而消亡。如果在给定的时间片内没有执行结束,就会被系统换下来回到等待执行状态。
  • 阻塞状态:
    • 处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备资源,将让出CPU并暂停自己的运行,进入阻塞状态。在阻塞状态的线程不能进入就绪队列,只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态在等待队列中等待,被系统选中后从原来停止的位置开始继续运行。
  • 死亡状态:
    • 死亡状态时线程声明周期的最后一个阶段,线程死亡的原因有两个,一个时正常运行的线程完成了它的全部工作;另一个时线程被强制终止(如通过执行Thread类的stop/destory方法来终止——不推荐,前者会产生异常导致程序终止,后者是强制终止不会释放锁)

二,停止线程

  1. 自然终止:线程体正常执行完毕

  2. 外部干涉:

    1. 线程类中 定义 线程体使用的标识
    2. 线程体使用该标识
    3. 提供对外的方法改变该标识
    4. 外部根据条件调用该方法即可
    实例
    
    package state;
    public class Demo01 {
    	public static void main(String[] args) {
    		Study s=new Study();
    		new Thread(s).start();
    		//外部干涉
    		for(int i=0;i<500;i++) {
    			if(i==50) {
    				s.stop();
    			}
    			System.out.println("main...."+i);
    		}
    	}
    }
    class Study implements Runnable{
    	//第一步,线程类中定义线程体使用的标识
    	private boolean flag=true;
    	public void run() {
    		//第二步,线程体使用该表示
    		while(flag) {
    			System.out.println("study thread...");
    		}
    	}
    	//第三步,对外提供方法改变该标识
    	public void stop() {
    		this.flag=false;
    	}
    }
    

三,阻塞

  1. join :合并线程

    1. 线程对象.join():等待该线程终止

      实例
      
      package state;
      /*
       join():合并线程
       */
      public class JoinDemo01 extends Thread{
      	public static void main(String[] args) throws InterruptedException {
      		JoinDemo01 demo=new JoinDemo01();
      		Thread t=new Thread(demo);//新生
      		t.start();//就绪
      		//Cpu调度运行
      		for(int i=0;i<1000;i++) {
      			if(i==50) {
      				/*
      				 * CPU随机调度,或者使用join方法
      				 */
      				t.join();//合并线程,等待t线程执行完毕后再执行main线程(main阻塞)
      			}
      			System.out.println("main..."+i);
      		}
      	}
      	@Override
      		public void run() {
      			for(int i=0;i<100;i++) {
      				System.out.println("join..."+i);
      			}
      		}
      }
      
  2. static yield:暂停线程

    • Thread.yield():在当前时间片暂停所在线程,并执行其它线程

    • 实例
      
      package state;
      public class YieldDemo01 extends Thread{
      	public static void main(String[] args) {
      		YieldDemo01 demo=new YieldDemo01();
      		Thread t=new Thread(demo);//新生
      		t.start();//就绪
      		//Cpu调度运行
      		for(int i=0;i<1000;i++) {
      			if(i%20==0) {
      				//暂停本线程main
      				Thread.yield();//在谁的线程体中就停止谁
      			}
      			System.out.println("main..."+i);
      		}
      	}
      	@Override
      	public void run() {
      		for(int i=0;i<100;i++) {
      			System.out.println("yield..."+i);
      		}
      	}
      }
      
  3. sleep:休眠,不释放锁

    1. 与时间相关:倒计时

      1. 实例
        
        package state;
        import java.text.SimpleDateFormat;
        import java.util.Date;
        /*
         倒计时
         1,倒数10个数,一秒内打印一个
         */
        public class SleepDemo01 {
        	public static void main(String[] args) throws InterruptedException {
        		Date endTime=new Date(System.currentTimeMillis()+10*1000);//10秒后
        		long end=endTime.getTime();
        		while(true) {
        			//输出
        			System.out.println(new SimpleDateFormat("mm:ss").format(endTime));
        			//构建上一秒时间--回溯
        			endTime=new Date(endTime.getTime()-1000);
        			//等待1秒
        			Thread.sleep(1000);
        			//10秒内继续否则推出
        			if(end-10000>endTime.getTime()) {
        				break;
        			}
        		}
        	}
        }
        
    2. 模拟网络延时 (并发)

线程基本信息和优先级

方法 功能
isAlive() 判断线程是否还“活"着,即线程是否还未终止
getPriority() 获得线程的优先级数值
setPriority() 设置线程的优先级数值
setName() 给线程一个名字
getName() 取得线程的名字
currentThread() 取得当前运行的线程对象,即取得自身
实例
线程对象
public class MyThread implements Runnable {
	private boolean flag =true;
	private int num =0;
	@Override
	public void run() {
		while(flag){
			System.out.println(Thread.currentThread().getName()+"-->"+num++);
		}
	}
	public void stop(){
		this.flag=!this.flag;
	}
}
测试类1
/**
 *
  Thread.currentThread()	 :当前线程
  setName():设置名称
  getName():获取名称
  isAlive():判断状态
 * @author Administrator
 *
 */
public class InfoDemo01 {
	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		MyThread it =new MyThread();
		Thread proxy =new Thread(it,"挨踢");
		proxy.setName("test");
		System.out.println(proxy.getName());
		System.out.println(Thread.currentThread().getName()); //main
		proxy.start();
		System.out.println("启动后的状态:"+proxy.isAlive());
		Thread.sleep(200);
		it.stop();
		Thread.sleep(100);
		System.out.println("停止后的状态:"+proxy.isAlive());
	}
}
测试类2
/**
 * 优先级:概率,不是绝对的先后顺序
   MAX_PRIORITY  10
   NORM_PRIORITY 5 (默认)
   MIN_PRIORITY  1
   setPriority()
   getPriority()
 * @author Administrator
 *
 */
public class InfoDemo02 {
	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		MyThread it =new MyThread();
		Thread p1 =new Thread(it,"挨踢1");
		MyThread it2 =new MyThread();
		Thread p2 =new Thread(it2,"挨踢2");
		p1.setPriority(Thread.MIN_PRIORITY); //设置优先级
		p2.setPriority(Thread.MAX_PRIORITY);//设置优先级
		p1.start();
		p2.start();
		Thread.sleep(100);
		it.stop();
		it2.stop();
	}
}

线程的同步和死锁问题

线程的同步
  • 由于统一进程的多个线程共享同一片存储空间,带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门的机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。

  • 由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,定义同步锁的修饰语,包括两种用法:

    • synchronized 方法
    • synchronized(对象)块
    修饰非静态方法和对象时
    
    public class SynDemo01 {
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		//真实角色
    		Web12306 web= new Web12306();
    		//代理
    		Thread t1 =new Thread(web,"路人甲");
    		Thread t2 =new Thread(web,"黄牛已");
    		Thread t3 =new Thread(web,"攻城师");
    		//启动线程
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    }
    /**
     * 线程安全的类
     * @author Administrator
     *
     */
    class Web12306 implements Runnable {
    	private int num =10;
    	private boolean flag =true;
    	@Override
    	public void run() {
    		while(flag){
    			test5();
    		}
    	}
    	public void test6(){
    		if(num<=0){
    			flag=false; //跳出循环
    			return ;
    		}
    		 //a  b  c 	
    		synchronized(this){
    			try {
    				Thread.sleep(500); //模拟 延时
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
    		}
    	}
    	//线程不安全  锁定资源不正确
    	public void test5(){
    		//锁住了成员变量,没有锁住对象,而多线程并行访问对象时直接穿透锁造成资源不一致
    		synchronized((Integer)num){
    			if(num<=0){
    				flag=false; //跳出循环
    				return ;
    			}
    			//没有锁住整个对象,多线程可能在某个时间片直接穿透抵达此处,比如num剩下1时,a线程穿过if的表达式后释放了锁,b线程紧随拿到锁。
    			try {
    				Thread.sleep(500); //模拟 延时
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
    		}
    	}
    	//锁定范围不正确 线程不安全
    	public void test4(){
    		//   c  1
    		synchronized(this){
    			//b
    			if(num<=0){
    				flag=false; //跳出循环
    				return ;
    			}
    		}
    		// b
    		try {
    			Thread.sleep(500); //模拟 延时
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
    	}//a -->1
    	//线程安全  锁定正确
    	public void test3(){
    		//a  b  c
    		synchronized(this){
    			if(num<=0){
    				flag=false; //跳出循环
    				return ;
    			}
    			try {
    				Thread.sleep(500); //模拟 延时
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
    		}
    	}
    	//线程安全  锁定正确
    	public synchronized void test2(){
    		if(num<=0){
    			flag=false; //跳出循环
    			return ;
    		}
    		try {
    			Thread.sleep(500); //模拟 延时
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
    	}
    	//线程不安全
    	public void test1(){
    		if(num<=0){
    			flag=false; //跳出循环
    			return ;
    		}
    		try {
    			Thread.sleep(500);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
    	}
    }
    
    当只锁住了成员变量而没有锁住对象时,多线程并行访问对象在num比较完就直接穿透锁造成资源不一致
  • 单例设计模式:确保一个类在外部使用时,只有一个对象。

    实例
    
    package com.bjsxt.thread.syn;
    /**
     * 单例设计模式:确保一个类只有一个对象
     * @author Administrator
     *
     */
    public class SynDemo02 {
    	/**
    	 * @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.getInstance(time));
    	}
    }
    /**
     * 单例设计模式
     * 确保一个类只有一个对象
     * 懒汉式  double checking
     * 1、构造器私有化,避免外部直接创建对象
     * 2、声明一个私有的静态变量
     * 3、创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象
     */
    class Jvm {
    	//声明一个私有的静态变量
    	private static Jvm instance =null;	
    	//构造器私有化,避免外部直接创建对象
    	private Jvm(){
    	}
    	//创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象
    	public static Jvm getInstance(long time){
    		// c d e  -->效率  提供 已经存在对象的访问效率
    		if(null==instance){	
    			// a b
    			synchronized(Jvm.class){
    				if(null==instance ){
    					try {
    						Thread.sleep(time); //延时 ,放大错误
    					} catch (InterruptedException e) {
    						e.printStackTrace();
    					}
    					instance =new Jvm();
    				}
    			}
    	  }//a
    	  return instance;
    	}
    	public static Jvm getInstance3(long time){
    		//a b c d e  -->效率不高 c  存在对象也需要等待
    		synchronized(Jvm.class){
    			if(null==instance ){
    				try {
    					Thread.sleep(time); //延时 ,放大错误
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    				instance =new Jvm();
    			}
    			return instance;
    		}
    	}
    	public static synchronized Jvm getInstance2(long time){
    		if(null==instance ){
    			try {
    				Thread.sleep(time); //延时 ,放大错误
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			instance =new Jvm();
    		}
    		return instance;
    	}
    	public static Jvm getInstance1(long time){
    		if(null==instance ){
    			try {
    				Thread.sleep(time); //延时 ,放大错误
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			instance =new Jvm();
    		}
    		return instance;
    	}
    }
    单例设计模式的多种写法
    
    /**
     * 单例创建的方式
     * 1、懒汉式
     * 1)、构造器私有化
     * 2)、声明私有的静态属性
     * 3)、对外提供访问属性的静态方法,确保该对象存在
     * 
     * @author Administrator
     *
     */
    public class MyJvm {
    	private static MyJvm instance;
    	private MyJvm(){
    	}
    	public static MyJvm getInstance (){
    		if(null==instance){ //提供效率
    			synchronized(MyJvm.class){
    				if(null==instance){ //安全
    					instance =new MyJvm();
    				}
    			}
    		}
    		return instance;
    	}
    }
    /**
     * 饿汉式
       1)、构造器私有化 
     * 2)、声明私有的静态属性,同时创建该对象
     * 3)、对外提供访问属性的静态方法
     * @author Administrator
     *
     */
    class MyJvm2 {
    	private static MyJvm2 instance =new MyJvm2();//类加载时初始化
    	private MyJvm2(){
    	}
    	public static MyJvm2 getInstance (){		
    		return instance;
    	}
    }
    /**
     * 类在使用的时候加载 ,延缓加载时机
     * @author Administrator
     *
     */
    class MyJvm3 {
    	private static class JVMholder{//类加载时不初始化内部类
    		private static MyJvm3 instance =new MyJvm3();
    	}
    	private MyJvm3(){
    	}
    	public static MyJvm3 getInstance (){		
    		return JVMholder.instance;//调用方法时初始化内部类
    	}
    }
    死锁
    
    package com.bjsxt.thread.syn;
    /*
     * 过多的同步方法容易造成死锁
     */
    public class Demo {
    	public static void main(String[] args) {
    		Object g=new Object();
    		Object m=new Object();
    		TestA t1=new TestA(g,m);
    		TestB t2=new TestB(g,m);
    		Thread p1=new Thread(t1);
    		Thread p2=new Thread(t2);
    		p1.start();
    		p2.start();
    	}
    }
    class TestA implements Runnable{
    	Object goods;
    	Object money;
    	public TestA(Object goods,Object money) {
    		this.goods=goods;
    		this.money=money;
    	}
    	@Override
    	public void run() {
    		while(true) {
    			try {
    				test();
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    	public void test() throws InterruptedException {
    		synchronized(goods) {
    			Thread.sleep(100);
    			synchronized(money) {x
    			}
    		}
    		System.out.println("一手给钱");
    	}
    }
    class TestB implements Runnable{
    	Object goods;
    	Object money;
    	public TestB(Object goods,Object money) {
    		this.goods=goods;
    		this.money=money;
    	}
    	@Override
    	public void run() {
    		while(true) {
    			try {
    				test();
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    	public void test() throws InterruptedException {
    		synchronized(money) {
    			Thread.sleep(100);
    			synchronized(goods) {
    			}
    		}
    		System.out.println("一手给货");
    	}
    }

生产者消费者模式

生产者消费者问题(Producer-consumer problem)/有限缓冲问题(bounded-buffer problem),是多线程同步问题的经典案例 。

  • 该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者"和”消费者“——在实际运行中会发生的问题。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

  • 要解决该问题,就必须让生产者在缓冲区空时进入休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据时,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用的方法有信号灯法、管程等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。

    信号灯法
    	
    Movie
    
    	package com.bjsxt.thread.pro;
    /**
     一个场景,共同的资源
      生产者消费者模式 信号灯法
     wait() :等待,释放锁   sleep 不释放锁
     notify()/notifyAll():唤醒
      与 synchronized
     * @author Administrator
     *
     */
    public class Movie {
    	private String pic ;
    	//信号灯
    	//flag -->T 生产生产,消费者等待 ,生产完成后通知消费
    	//flag -->F 消费者消费 生产者等待, 消费完成后通知生产
    	private boolean flag =true;
    	/**
    	 * 播放
    	 * @param pic
    	 */
    	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;
    	}
    }
    		
    Player
    
    	package com.bjsxt.thread.pro;
    /**
     * 生产者
     * @author Administrator
     *
     */
    public class Player implements Runnable {
    	private Movie m ;
    	public Player(Movie m) {
    		super();
    		this.m = m;
    	}
    	@Override
    	public void run() {
    		for(int i=0;i<20;i++){
    			if(0==i%2){
    				m.play("左青龙");
    			}else{
    				m.play("右白虎");
    			}
    		}
    	}
    }
    		
    Watcher
    
    	package com.bjsxt.thread.pro;
    public class Watcher implements Runnable {
    	private Movie m ;
    	public Watcher(Movie m) {
    		super();
    		this.m = m;
    	}
    	@Override
    	public void run() {
    		for(int i=0;i<20;i++){
    			m.watch();
    		}
    	}
    }
    		
    App
    
    	package com.bjsxt.thread.pro;
    public class App {
    	public static void main(String[] args) {
    		//共同的资源
    		Movie m = new Movie();
    		//多线程
    		Player p = new Player(m);
    		Watcher w = new Watcher(m);
    		new Thread(p).start();		
    		new Thread(w).start();
    	}
    }
    	
    管程法
    
    package com.bjsxt.thread.pro;
    public class TestProduce {
    	public static void main(String[] args) {
    		SyncStack sStack = new SyncStack();
    		Shengchan sc = new Shengchan(sStack);
    		Xiaofei xf = new Xiaofei(sStack);
    		sc.start();
    		xf.start();
    	}
    }
    class Mantou {
    	int id;
    	Mantou(int id){
    		this.id=id;
    	}
    }
    class SyncStack{
    	int index=0;
    	Mantou[] ms = new Mantou[10];
    	public synchronized void push(Mantou m){
    		while(index==ms.length){
    			try {
    				this.wait(); 
    				//wait后,线程会将持有的锁释放。sleep是即使睡着也持有互斥锁。
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		this.notify(); //唤醒在当前对象等待池中等待的第一个线程。notifyAll叫醒所有在当前对象等待池中等待的所有线程。
    		//如果不唤醒的话。以后这两个线程都会进入等待线程,没有人唤醒。
    		ms[index]=m;
    		index++;
    	}
    	public synchronized Mantou pop(){
    		while(index==0){
    			try {
    				this.wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		this.notify();
    		index--;
    		return ms[index];
    	}
    }
    class Shengchan extends Thread{
    	SyncStack ss = null;
    	public Shengchan(SyncStack ss) {
    		this.ss=ss;
    	}
    	@Override
    	public void run() {
    		for (int i = 0; i < 20; i++) {
    			System.out.println("造馒头:"+i);
    			Mantou m = new Mantou(i);
    			ss.push(m);
    		}
    	}
    }
    class Xiaofei extends Thread{
    	SyncStack ss = null;
    	public Xiaofei(SyncStack ss) {
    		this.ss=ss;
    	}
    	@Override
    	public void run() {
    		for (int i = 0; i < 20; i++) {
    			Mantou m = ss.pop();
    			System.out.println("吃馒头:"+i);
    		}
    	}
    }
    

任务调度

  • Timer定时器类

  • TimerTask任务类

    实例
    package com.bjsxt.thread.schedule;
    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 Administrator
     *
     */
    public class TimeDemo01 {
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		Timer timer =new Timer();
    		timer.schedule(new TimerTask(){
    			@Override
    			public void run() {
    				System.out.println("so easy....");
    			}}, new Date(System.currentTimeMillis()+1000), 200);
    	}
    }
    
posted @ 2021-05-20 20:40  破晓云鸿  阅读(54)  评论(0编辑  收藏  举报