java基础学习笔记(六)--多线程技术

  1. 多线程基本概念 _ 程序 _ 进程 _ 线程
    1. 程序、进程、线程 (这条路叫做进程,里面的两个车道可以看做线程,线程共享进程的内存资源,两个车道的允许效率高,可以有多条车道同时运行,多个线程共享代码和内存空间,在同一个地址切换的切换速度快,进程的切换是内存地址的切换,进程是在操作系统中,线程是在同一应用程序中有多个顺序流同时执行)
      程序 : Program, 是一个指令的集合
      进程 : Process,( 正在执行中的程序 ) 是一个静态的概念进程是程序的一次静态态执行过程, 占用特定的地址空间 .每个进程都是独立的,由 3 部分组成 cpu,data,code
      缺点: 内存的浪费, cpu 的负担
      线程 : 是进程中一个“单一的连续控制流程” (a singlesThread,equential flow of control)/执行路径
      线程又被称为轻量级进程 (lightweight process)
      Threads run at the same time, independently of one another
      一个进程可拥有多个并行的(concurrent)线程
      一个进程中的线程共享相同的内存单元 / 内存地址空间 ? 可以访问相同的变量和对象,而且它们从同一堆中分配对象?
      信、数据交换、同步操作
      由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快。
    2. 进程与线程之间的区别
      区别进程线程
      根本区别作为资源分配的单位作为调度和执行的单位
      开销
      每个进程都有独立的代码和数据空间(进程上下文 ) ,进程
      间的切换会有较大的开销。
      线程可以看成时轻量级的进程,同一
      类线程共享代码和数据空间,每个线
      程有独立的运行栈和程序计数器(PC)
      线 程 切 换 的 开 销小。
      所处环境
      在操作系统中能同时运行多个任 务(程序 )
      在同一应用程序中有多个顺序流同时执行
      分配内存
      系统在运行的时候会为每个进程分配不同的内存区域
      除了 CPU 之外,不会为线程分配内存
      (线程所使用的资 源是它所属的进程
      的资源),线程组只能共享资源
      包含关系
      没有线程的进程是可以被看作单线程的,如果一个进程
      内拥有多个线程,则执行过程不是一条线的,而是多条
      线(线程)共同完成的。
      线程是进程的一部分,所以线程有的 时候被称为
      是轻权进程或者轻量级进程。
      一个进程中包含了 N 多,线程 , 如果线程结束,进程并不一定结束,
      进程结束,线程都将结束
      CPU 调度执行的是线程
  2. 通过继承 Thread 类实现多线程
    1. 实现多线程的步骤
      (1) 继承 Thread
      (2) 重写 run() 方法
      (3) 通过 start() 方法启动线程
      一定的缺点:
      Java 中的类是单继承的,一旦继承了 Thread 类,就不允许再去继承其它的类.
      public class Test01 extends Thread{
          public void run() {
          /*线程体:每个线程都执行的内容*/
             for(int i=0;i<10;i++){
             System.out.println("mystread1......"+i);
             }
          }
      }
      
      public class Test03 {
          public static void main(String[] args) {
              Test01 test01=new Test01();
              //start()方法导致此线程开始执行,java虚拟机调用此线程的run方法
              test01.start();//start线程启动以后,有两个线程,主线程和test01线程,cpu调度哪个线程不知道
              //test01.run(),先执行run方法中的代码,在执行主线程的方法
              //主方法就是主线程
             for(int i=0;i<10;i++){
                      System.out.println("main......"+i);
                }
           }
      }

       

  3. 通过实现接口 Runnable 实现多线程
    1. 实现 Runnable 接口实现多线程的步骤
      (1) 编写类实现 Runnable 接口
      (2) 实现 run() 方法
      (3) 通过 Thread 类的 start() 方法启动线程
      public class Test02 implements Runnable{
          public void run() {
              for(int i=0;i<10;i++){
              System.out.println("myrunnable..."+i);
              }
          }
      }
      public class Test {
          public static void main(String[] args) {
              Test02 my=new Test02();
              //my.start();
              Thread thread=new Thread(my);
              thread.start();
              for(int i=0;i<10;i++){
                  System.out.println("main ......"+i);
              }
          }
      }
    2. 静态代理模式
      Thread -> 代理角色
      MyRunnable -> 真实角色
      代理角色与真实角色实现共同的接口 Runnable 接口

      Runnable当中只有一个方法run(),Thread类中提供了很多方法用来操作线程

      结婚没有经验,不知道准备什么东西,所以找婚庆公司,婚庆公司就是代理角色,你就是我们的真实角色,共同拥有结婚的接口,这个接口只有一个结婚的方法,这个时候代理角色就可以给你做代理了

      public interface Marry {
          void marry();
      }
      public class MarryCompany implements Marry {
          //婚庆公司给准备结婚的人准备婚礼
          private Marry m;
          public MarryCompany(Marry m) {
              this.m = m;
          }
          public void before(){
              System.out.println("婚前准备");
          }
          public void after(){
              System.out.println("婚后");
          }
          @Override
          public void marry() {
              before();
              m.marry();//调用真实角色结婚的方法
              after();
           }
      }
      public class You implements Marry{
          public void marry() {
              System.out.println("你与有有结婚了");
          }
      }
      public class TestMarry {
          public static void main(String[] args) {
              Marry marry=new You();
              Marry mc=new MarryCompany(marry);
              mc.marry();
          }
      }
  4. 线程状态 _ 线程的生命周期
    1. 线程状态
      新生状态
      new 关键字建立一个线程后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用
      start() 方法进入就绪状态。
      就绪状态(万事具备,只欠CPU|有资格,无资源)
      处于就绪状态线程具备了运行条件,但还没分配到 CPU ,处于线程就绪队列,等待系统为其分配 CPU 。当系统选定一个等待执行的线程后,它就会从就绪状态进入执行状态,该动作称为“CPU 调度”。
      运行状态(有资格,有资源)
      在运行状态的线程执行自己的 run 方法中代码 , 直到等待某资源而阻塞或完成任何而死亡。如果在给定的时间片内没
      有执行结束,就会被系统给换下来回到等待执行状态。
      阻塞状态(有资源,无资格,让资源)
      处于运行状态的线程在某些情况下,如执行了 sleep( 睡眠 )方法,或等待 I/O 设备等资源,将让出 CPU 并暂时停止自己运行,进入阻塞状态。在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O 设备空闲下来,线程便转入就绪状态,重新到就绪队列 中排队等待,被系统选中后从原来停止的位置开始继续执行。
      死亡状态
      死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有三个,一个是正常运行的线程完成了它的全部工作;
      另一个是线程被强制性地终止,如通过 stop 方法来终止一个线程【不推荐使用】;三是线程抛出未捕获的异常。
  5. 获取线程基本信息的方法
    1. 线程操作的常用方法
      序号方法名称描述
      1
      static Thread currentThread()
      返回目前正在执行的线程
      2
      final String getName()
      返回线程的名称
      3
      final boolean isAlive()
       
      判断线程是否处于活动状态
      public class Test04 {
          //主方法,主线程
          public static void main(String[] args) {
              System.out.println(Thread.currentThread());
              MyRunnable myRunnable=new MyRunnable();
              Thread t1=new Thread(myRunnable);
              Thread t2=new Thread(myRunnable);
              Thread t3=new Thread(myRunnable);
              t1.start();
              t2.start();
              t3.start();
              /*在Thread类中一定有一个静态变量int,用于统计线程启动的个数*/
          }
      }
      class MyRunnable implements Runnable{
          @Override
          public void run() {
              Thread thread=Thread.currentThread();
              System.out.println(thread);
          }
      }

      新生状态的线程处于非活动状态,处于就绪状态的线程处于活动状态,如果主线程没有执行完,my线程执行结束,则处在非活动状态,如果主线程执行结束处于死亡状态,my线程没有执行完毕,则处在活动状态

  6. 暂停线程执行 sleep_yield_join_stop
    1. ​​​​​​​暂停线程执行的方法
      序号方法名称描述
      1
      final void join()
      调用该方法的线程强制执行,其它线程处于阻
      塞状态,该线程执行完毕后,其它线程再执行
      2
      static void sleep(long millis)
      使当前正在执行的线程休眠 millis , 线程处
      于阻塞状态
      3
      static void yield()
      当前正在执行的线程暂停一次,允许其他线程
      执行 , 不阻塞,线程进入就绪状态, 如果没有其他
      等待执行的线程,这个时候当前线程就会马上
      恢复执行。
      4
      final void stop()
      强迫线程停止执行。已过时。不推荐使用。
    2. 总结
      1.sleep
      不会释放锁, Sleep 时别的线程也不可以访问锁定对象。
      2.yield:
      让出 CPU 的使用权,从运行态直接进入就绪态。让 CPU重新挑选哪一个线程进入运行状态。
      3.join:
      当某个线程等待另一个线程执行结束后,才继续执行时,使调用该方法的线程在此之前执行完毕,也就是等待调
      用该方法的线程执行完毕后再往下继续执行
  7. 线程的优先级问题
    1. ​​​​​​​设置和获取线程优先级的方法
      序号方法名称描述
      1
      final int getPriority()
      获取线程的优先级
      2
      final void setPriority(int priority)
      设置线程的优先级
      /*
      * 线程的优先级问题
      *优先级越高越可能先被调用执行,但是不一定
      * */
      public class Test05 {
          public static void main(String[] args) {
              System.out.println(Thread.MAX_PRIORITY);//10
              System.out.println(Thread.MIN_PRIORITY);//1
              System.out.println(Thread.NORM_PRIORITY);//5
              Thread t=Thread.currentThread();
              //主线程的默认优先级是5
              System.out.println(t.getPriority());//5
              MyThread myThread=new MyThread();
              Thread thread=new Thread(myThread);
              //新创建的线程默认优先级也是5
              System.out.println(thread.getPriority());
              thread.setPriority(6);
              //thread.setPriority(100);优先级只能是1-10之间的,
          }
      }
      class MyThread implements Runnable{
          @Override
          public void run() {
          }
      }
      

       

  8. 多线程的安全性问题
    1. ​​​​​​​多线程的安全性问题
      public class Ticket implements Runnable{
          private static int ticket=5;
          @Override
          public void run() {
              for(int i=1;i<100;i++){//有100个人排队买票
                  if(ticket>0){
                      System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票");
                  }
              }
          }
      }
      public class TestTicket {
          public static void main(String[] args) {
              Ticket t=new Ticket();
              Thread t1=new Thread(t);
              Thread t2=new Thread(t);
              Thread t3=new Thread(t);
              t1.start();
              t2.start();
              t3.start();
          }
      }

       

  9. 线程同步 _ 具体实现
    1. ​​​​​​​同步实现的方式
      public class Ticket1 implements Runnable{
          private static int ticket=5;
          @Override
          public void run() {
              for(int i=1;i<100;i++){
                  synchronized (this){
                      if(ticket>0){
                          System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票");
                      }
                  }
              }
          }
      }
      

       

    2. 同步方法
      public class Ticket2 implements Runnable{
          private static int ticket=5;
          @Override
          public void run() {
              for(int i=1;i<100;i++){
                  saleTicket();
              }
          }
          private synchronized void saleTicket(){
              if(ticket>0){
                  System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票");
              }
          }
      }

       

  10. 死锁 _ 死锁的解决办法
    1. ​​​​​​​死锁产生的原因:多线程操作时,互相等待对方的资源
      线程 0x3704: 有钱 -> 买水
      线程 0x7954 :有水 -> 收钱
      获取“钱”的锁成功
       
      线程中断,等待下次被系统挑中执行
       
       
      获取“水”成功
       
      线程中断,等待下次被系统挑中执行
      获取“水”的锁 : 失败,继续等待
       
       
      获取“钱”的锁失败,继续等待
      public class DeadLock extends Thread {
      	private Object money;//钱
      	private Object water;//水
      	public boolean flag;//标识持有对象锁
      	
      	public DeadLock(Object money, Object water) {
      		super();
      		this.money = money;
      		this.water = water;
      	}
      
      	@Override
      	public void run() {
      		if(flag){//true时,持有“钱”的锁
      			synchronized (money) {
      				System.out.println("有钱,等水");
      				synchronized (water) {
      					System.out.println("有水,等钱");
      				}
      			}
      		}else{
      			synchronized (water) {
      				System.out.println("有水,等钱");
      				synchronized (money) {
      					System.out.println("有钱,等水");
      				}
      			}
      			
      		}
      	}
      }

       

    2. 如何解决死锁: 不要让两个对象同时持有对象锁,采用互斥方式来解决
      public void run(){
              if(flag){//true时,持有钱的锁
                  synchronized (money){
                      System.out.println("有钱等水");
                  }
                  synchronized (water){
                      System.out.println("有水等钱");
                  }
              }else{
                  synchronized (water){
                      System.out.println("有水等钱");
                  }
                  synchronized (money){
                      System.out.println("有钱等水");
                  }
              }
          }
    3. 银行家算法
      银行家算法: 该算法需要检查申请者对资源的最大需求量,如果系统现存的各类资源可以满足申请者的请求,就满足申请者的请求。这样申请者就可很快完成其计算,然后释放它占用的资源,从而保证了系统中的所有进程都能完成,所以可避免死锁的发生。(计算资源的大小,计算出来后,永远按照从大到小的方式来获得锁)
  11. 生产者消费者模式的实现1
    1. 生产者与消费者原理
      public class Goods {
      	private String name;//名称
      	private String brand;//品牌
      	public String getName() {
      		return name;
      	}
      	public void setName(String name) {
      		this.name = name;
      	}
      	public String getBrand() {
      		return brand;
      	}
      	public void setBrand(String brand) {
      		this.brand = brand;
      	}
      	public Goods(String name, String brand) {
      		super();
      		this.name = name;
      		this.brand = brand;
      	}
      	public Goods() {
      		super();
      	}
      	
      }
      
      public class Customer implements Runnable {
      	private Goods goods;
      	public Customer(Goods goods){
      		this.goods=goods;
      	}
      	public void run() {
      		for(int i=0;i<10;i++){
      			try {
      				Thread.sleep(300);
      			} catch (InterruptedException e) {
      				// TODO Auto-generated catch block
      				e.printStackTrace();
      			}
      			System.out.println("-----消费者线程取了"+goods.getBrand()+"----------------"+goods.getName());
      		}
      		
      	};
      }
      
      public class Producter implements Runnable{
      	private Goods goods;
      	public Producter(Goods goods){
      		this.goods=goods;
      	}
      	@Override
      	public void run() {
      		//生产商品
      		for(int i=0;i<10;i++){
      			if(i%2!=0){//奇数
      				goods.setBrand("旺仔");
      				try {
      					Thread.sleep(300);
      				} catch (InterruptedException e) {
      					// TODO Auto-generated catch block
      					e.printStackTrace();
      				}
      				goods.setName("小馒头");
      			}else{
      				goods.setBrand("娃哈哈");
      				try {
      					Thread.sleep(300);
      				} catch (InterruptedException e) {
      					// TODO Auto-generated catch block
      					e.printStackTrace();
      				}
      				goods.setName("矿泉水");
      			}
      			System.out.println("生产者线程生产了"+goods.getBrand()+"--------------------"+goods.getName());
      		}
      	}
      
      }
      
      public class Test {
      	public static void main(String[] args) {
      		//创建共享资源对象
      		Goods g=new Goods();
      		//创建生产者线程
      		Producter p=new Producter(g);
      		//创建生产者线程
      		Customer c=new Customer(g);
      		new Thread(p).start();
      		new Thread(c).start();
      	}
      }

       

    2. 产生的问题
      数据错乱
      解决方案 -> 线程同步
      public class Goods {
      	private String name;//名称
      	private String brand;//品牌
      	public String getName() {
      		return name;
      	}
      	public void setName(String name) {
      		this.name = name;
      	}
      	public String getBrand() {
      		return brand;
      	}
      	public void setBrand(String brand) {
      		this.brand = brand;
      	}
      	public Goods(String name, String brand) {
      		super();
      		this.name = name;
      		this.brand = brand;
      	}
      	public Goods() {
      		super();
      	}
      	//编写一个赋值的方法  同步监视器为Goods类的对象
      	public synchronized void set(String name,String brand){
      		this.setName(name);
      		try {
      			Thread.sleep(300);
      		} catch (InterruptedException e) {
      			// TODO Auto-generated catch block
      			e.printStackTrace();
      		}
      		this.setBrand(brand);
      		System.out.println("-------生产者线程生产了-------"+this.getBrand()+"-------"+this.getName());
      	}
      	//编写一个取值的方法
      	public synchronized void get(){
      		System.out.println("消费者线程取走了------"+this.getBrand()+"--------"+this.getName());
      	}
      	
      }
      
      public class Customer implements Runnable {
      	private Goods goods;
      	public Customer(Goods goods){
      		this.goods=goods;
      	}
      	public void run() {
      		for(int i=0;i<10;i++){
      			goods.get();//调用商品类中的取值的方法
      		}
      		
      	};
      }
      
      public class Producter implements Runnable{
      	private Goods goods;
      	public Producter(Goods goods){
      		this.goods=goods;
      	}
      	@Override
      	public void run() {
      		//生产商品
      		for(int i=0;i<10;i++){
      			if(i%2!=0){//奇数
      				goods.set("小馒头", "旺仔");//调用商品类的同步方法
      			}else{
      				goods.set("矿泉水", "娃哈哈");
      			}
      			
      		}
      	}
      
      }
      public class Test {
      	public static void main(String[] args) {
      		//创建共享资源对象
      		Goods g=new Goods();
      		//创建生产者线程
      		Producter p=new Producter(g);
      		//创建生产者线程
      		Customer c=new Customer(g);
      		new Thread(p).start();
      		new Thread(c).start();
      	}
      }
      

       

  12. 生产者消费者模式的实现2
    1. 生产者 - 消费者模式产生的问题 2
      重复生产和重复取走
      解决方案 -> 线程间的通信
    2. 线程间通信的方法
      wait(): 调用了 wait() 方法的线程进入等待池进行等待,等待池中的线程不去竞争对象锁, 直到其它的线程通知,才会进入锁池
      notify(): 随机唤醒一个在该对象上等待的线 程, 被唤醒的线程进行锁池,开始竞争该对锁上的锁
      notifyAll(): 唤醒所有在该对象上等待的线程优先级高的线程有可能先竞争到对象锁只能在同步方法和同步代码块中使用
      public class Goods {
      	private String name;//名称
      	private String brand;//品牌
      	private boolean isFlag;//用于标识是否有商品 ,假设为true时代表有商品,false时代表没有商品
      	public String getName() {
      		return name;
      	}
      	public void setName(String name) {
      		this.name = name;
      	}
      	public String getBrand() {
      		return brand;
      	}
      	public void setBrand(String brand) {
      		this.brand = brand;
      	}
      	public Goods(String name, String brand) {
      		super();
      		this.name = name;
      		this.brand = brand;
      	}
      	public Goods() {
      		super();
      	}
      	//编写一个赋值的方法  同步监视器为Goods类的对象
      	public synchronized void set(String name,String brand){
      		if(isFlag){//相当于isFlag==true
      			try {
      				super.wait();//生产者线程等待
      			} catch (InterruptedException e) {
      				// TODO Auto-generated catch block
      				e.printStackTrace();
      			}
      		}  //当生产者线程被唤醒后从wait()之后的代码开始执行
      		//生产商品
      		this.setName(name);
      		try {
      			Thread.sleep(300);
      		} catch (InterruptedException e) {
      			// TODO Auto-generated catch block
      			e.printStackTrace();
      		}
      		this.setBrand(brand);
      		System.out.println("-------生产者线程生产了-------"+this.getBrand()+"-------"+this.getName());
      		//通知消费者
      		super.notify();
      		isFlag=true;
      	}
      	//编写一个取值的方法
      	public synchronized void get(){
      		if(!isFlag){  // 相不于isFlag==false
      			try {
      				super.wait();
      			} catch (InterruptedException e) {
      				// TODO Auto-generated catch block
      				e.printStackTrace();
      			}//消费者等待
      		}//消费者线程被唤醒后从wait()之后开始执行
      		
      		System.out.println("消费者线程取走了------"+this.getBrand()+"--------"+this.getName());
      		super.notify();//通知生产者线程
      		isFlag=false;//没有商品
      	}
      	
      }
      
      public class Customer implements Runnable {
      	private Goods goods;
      	public Customer(Goods goods){
      		this.goods=goods;
      	}
      	public void run() {
      		for(int i=0;i<10;i++){
      			goods.get();//调用商品类中的取值的方法
      		}
      		
      	};
      }
      
      public class Producter implements Runnable{
      	private Goods goods;
      	public Producter(Goods goods){
      		this.goods=goods;
      	}
      	@Override
      	public void run() {
      		//生产商品
      		for(int i=0;i<10;i++){
      			if(i%2!=0){//奇数
      				goods.set("小馒头", "旺仔");//调用商品类的同步方法
      			}else{
      				goods.set("矿泉水", "娃哈哈");
      			}
      			
      		}
      	}
      
      }
      
      public class Test {
      	public static void main(String[] args) {
      		//创建共享资源对象
      		Goods g=new Goods();
      		//创建生产者线程
      		Producter p=new Producter(g);
      		//创建生产者线程
      		Customer c=new Customer(g);
      		new Thread(p).start();
      		new Thread(c).start();
      	}
      }
      

       

 


编写两个线程 , 一个线程打印 1-52 的整数,另一个线程打印字母 A-Z 。打印顺序为 12A34B56C .5152Z 。即按照整数和字
母的顺序从小到大打印,并且每打印两个整数后,打印一个字母,交替循环打印,直到打印到整数 52 和字母 Z 结束。
 
public class Printer {
    private int index=1;//用于统计第几次打印
    public synchronized void print(int number){
        while(index%3==0){
            try {
                super.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.print(number);
        index++;
        super.notifyAll();//唤醒在Printer这个对象上的所有的等待的线程
    }
    public synchronized void print(char letter){
        while(index%3!=0){
            try {
                super.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.print(""+letter);
        index++;
        super.notifyAll();
    }
}
public class NumberPrinter implements Runnable {
    private Printer printer;
    public NumberPrinter(Printer printer) {
        this.printer=printer;
    }

    @Override
    public void run() {
        for(int i=1;i<=52;i++){
            printer.print(i);
        }

    }
}
public class LetterPrinter implements Runnable {
    private Printer printer;
    public LetterPrinter(Printer printer) {
        this.printer=printer;
    }
    @Override
    public void run() {
        for(char c='A';c<='Z';c++){
            printer.print(c);
        }

    }
}
public class Test {
    public static void main(String[] args) {
        //(1)创建共享资源的对象
        Printer p=new Printer();
        NumberPrinter np=new NumberPrinter(p);
        LetterPrinter lp=new LetterPrinter(p);
        //创建代理类,并启线程
        new Thread(np).start();
        new Thread(lp).start();
    }
}

实现多线程的第三种方式

  1. 实现线程前两种方式的缺点
    1) 没有返回值
    2) 不支持泛型
    3) 异常必须处理
    ​​​​​​​
  2. 实现多线程的第三种方式
    实现 Callable 接口,重写 call 方法
    Callable 功能更加强大
    1) Future 接口位于 java.util.concurrent 包中 , 可以对具体Runnable、 Callable 任务的执行结果进行取消 (cancel 方法,
    尝试取消执行此任务 ) 、查询是否完成 (isDone 方法 ) 、获取结果(get 方法 , 等待完成,然后检索其结果 ) 等。
    2) FutrueTask Futrue 接口的唯一的实现类
    3) FutureTask 同时实现了 Runnable, Future 接口。它既可以作为 Runnable 被线程执行,又可以作为 Future 得到
    Callable 的返回值
    public class Test01 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //创建任务
            MyCallable call=new MyCallable();
            //交给任务管理器
            /*任务管理器是一个实现类,实现了RunnableFutrue接口
            * RunnableFutrue是Future和Runnable的子接口*/
            FutureTask<String> task=new FutureTask<>(call);
            Thread t=new Thread(task);
            t.start();
            System.out.println("获取结果:"+task.get());
            //判断任务是否完成
            System.out.println(task.isDone());//true
        }
    }
    //有了泛型,有了返回值,还可以抛异常
    public class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            String [] str={"apple","banana","orange","pear","grape"};
            int index=(int)(Math.random()*5);
            return str[index];
        }
    }
    

线程同步第三种方式线程同步 Lock

  1. 线程同步的第三种方法
    Lock : 对需要上锁的地方上锁
    1) JDK1.5 后新增的功能
    2) Synchronized 相比, Lock 可提供多种锁方案,更灵活
    3) Java.util.concurrent.locks 中的 Lock 是一个接口,它的实现类是一个 Java 类,而不是作为语言的特性(关键字)来
    实现
    注意: 如果同步代码有异常,要将 unLock() 放到 finally
  2. 步骤
    1) 创建 Lock 对象
    2) 调用 lock() 方法上锁
    3) 调用 unlock() 方法解锁
  3. Lock synchronized 的区别
    1) Lock 是 显 示 锁 ( 手 动 开 启 和 关 闭 锁 , 别 忘 关 闭锁),synchronized 是隐式锁
    2) Lock 只有代码块锁, synchronized 有代码块锁和方法锁
    3) 使用 Lock 锁, JVM 将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)4) Lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待( 即被阻止 ) ,直到该对象被释放。 lock() 方法会对 Lock 实例对象进行加锁,因此所有对该对象调用 lock()方法的线程都会被阻塞,直到该 Lock 对象的 unlock() 方法被调用
    public class CountRunnable implements Runnable{
        private int count =0;
        Lock lock=new ReentrantLock();
        @Override
        public void run() {
            for(int i=0;i<10;i++){
                //synchronized (this){
                try {
                    lock.lock();
                    count++;
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"执行操作:count="+count);
                }finally {
                    lock.unlock();
                }
               // }
            }
        }
    }
    
    public class Test03 {
        public static void main(String[] args) {
            CountRunnable cr=new CountRunnable();
            Thread t1=new Thread(cr,"A");
            Thread t2=new Thread(cr,"B");
            Thread t3=new Thread(cr,"C");
            t1.start();
            t2.start();
            t3.start();
        }
    }
    

     


线程池

  1. 什么是线程池
    创建和销毁对象是非常耗费时间的
    创建对象: 需要分配内存等资源
    销毁对象: 虽然不需要程序员操心,但是垃圾回收器会在后台一直跟踪并销毁
    对于经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
    思路: 创建好多个线程,放入线程池中,使用时直接获取引用,不使用时放回池中。可以避免频繁创建销毁、实现重复
    利用
    生活案例: 共享单车,自己买一个,去哪里买,都需要时间问题,共享单车就是单车池
    技术案例: 线程池、数据库连接池 JDK1.5 起,提供了内置线程池
  2. 线程池的好处
    1) 提高响应速度(减少了创建新线程的时间)(空闲了就可以使用)
    2) 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
    3) 提高线程的可管理性:避免线程无限制创建、从而销毁系统资源,降低系统稳定性,甚至内存溢出或者 CPU 耗尽(单车也是有限的,一共投放十万台,用车高峰再投出两万台,过了高峰期再收回去)
  3. 线程池的应用场合
    1) 需要大量线程,并且完成任务的时间短(创建线程3秒,执行线程2秒,销毁线程是3秒,放在线程池,只需要创建一次)
    2) 对性能要求苛刻
    3) 接受突发性的大量请求
  4. 使用线程池执行大量的 Runnable 命令
    public class Test1 {
        public static void main(String[] args) {
            //如何创建一个线程池
            //1.创建一个线程池,线程池只有一个线程对象
            ExecutorService pool1= Executors.newSingleThreadScheduledExecutor();
            //2.创建一个线程池,线程池的线程对象固定
            ExecutorService pool2=Executors.newFixedThreadPool(10);
            //3.创建一个线程池,线程池线程数量可以动态改变
            ExecutorService pool3=Executors.newCachedThreadPool();
            //使用线程池执行大量的 Runnable 命令
            for(int i=0;i<20;i++){
                final int n=i;
                //使用匿名内部类创建任务
                Runnable command=new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("开始执行:"+n);
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("执行结束:"+n);
                    }
                };//任务结束
                //将任务交给线程池中的线程执行
                pool3.execute(command);
            }
            //任务执行完成关闭线程池
            pool3.shutdown();
        }
    }
    
  5. 使用线程池执行大量的 Callable 任务
    public class Test2 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //如何创建一个线程池
            //1.创建一个线程池,线程池只有一个线程对象
            ExecutorService pool1= Executors.newSingleThreadScheduledExecutor();
            //2.创建一个线程池,线程池的线程对象固定
            ExecutorService pool2=Executors.newFixedThreadPool(10);
            //3.创建一个线程池,线程池线程数量可以动态改变
            ExecutorService pool3=Executors.newCachedThreadPool();
            List<Future> list=new ArrayList<>();
           // 使用线程池执行大量的 Callable 任务
            for(int i=0;i<20;i++){
                //创建任务
                Callable<Integer> task=new Callable<Integer>() {
                    @Override
                    public Integer call() throws Exception {
                        Thread.sleep(2000);
                        return (int)(Math.random()*10)+1;
                    }
                };//任务结束
                //将任务交给线程池
                Future f=pool2.submit(task);
                list.add(f);
               // System.out.println(f.get());//因为要输出结果,效率并没有提高?解决:将任务分发
            }
            for (Future ff:list){
                System.out.println(ff.get());
            }
        pool2.shutdown();
        }
    }
    

任务调度 Task_Timer

  1. 什么是调度
    任务:就是事情
    调度:在不同的时间点或者在指定的时间点或者间隔多长时间我去运行这个任务。
    就是生活中的闹钟
  2. 相关的类                                                                                                                                                                                      Timer :位于 java.util 包中
  3. 案例
    public class Clock extends TimerTask {
        long time=1000;//1秒
        @Override
        public void run() {
            Date date=new Date(time);
            System.out.println(date.toLocaleString());
            time+=1000;
        }
    }
    public class TestTimer {
        public static void main(String[] args) {
            //1.创建Timer对象
            Timer t=new Timer();
            //2.调用schedule执行任务
            //创建任务
            Clock c=new Clock();
                        //要执行的任务,任务的开始执行时间,每隔多长时间执行一次
            t.schedule(c,new Date(System.currentTimeMillis()+1000),1000);
    
        }
    }
    

     


ThreadLocal 是什么_JDK 底层原理

  1. ThreadLocal
    ThreadLocal 直译为“本地线程”,其实它就是一容器,用于存放线程的局部变量
    作用: 为解决多线程程序的并发问题
  2. 案例:实现一个序列号的生成器程序
    public interface Sequence {
        public int getNumber();//每次调用时获得一个数,下次调用时,这个数自增
    }
    public class SequenceImpl implements Sequence {
        //共享的资源
        private static int number=0;
        @Override
        public int getNumber() {
            number++;
            return number;
        }
    }
    public class MyThread implements Runnable {
        private Sequence seq;
    
        public MyThread(Sequence seq) {
            this.seq = seq;
        }
        @Override
        public void run() {
            for(int i=0;i<3;i++){
                System.out.println(Thread.currentThread().getName()+seq.getNumber());
            }
        }
    }
    public class Test3 {
        public static void main(String[] args) {
            //1.创建共享资源的对象
           // Sequence seq=new SequenceImpl();
            Sequence seq=new SequenceImpl2();
            //2.创建线程类的对象
            MyThread mh=new MyThread(seq);
            Thread t1=new Thread(mh,"A");
            Thread t2=new Thread(mh,"B");
            Thread t3=new Thread(mh,"C");
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    public class SequenceImpl2 implements Sequence {
        private static ThreadLocal<Integer> numberContainer=new ThreadLocal<Integer>(){
            protected Integer initialValue(){
                return 0;
            }
        };
        @Override
        public int getNumber() {
            numberContainer.set(numberContainer.get()+1);
            return numberContainer.get();
        }
    }
    

     

  3. ThreadLocal API
    get()  返回当前线程的此线程局部变量的副本中的值
    initialValue() 返回此线程局部变量的当前线程的初始值
    remove()删除此线程局部变量的当前线程的值
    set()将当前线程的此线程的局部变量的副本设置为指定的值
    withInitial(Supplier<? extends S> supplier)创建线程局部变量

 ThreadLocal 的使用场景

ThreadLocal 的使用场景为 : 用来解决数据库连接、Session管理等,每个线程需要有自己单独的实例,实例需要在多个方法中共享,但不希望被多线程共享​​​​​​​
 
ThreadLocal 在数据库连接上的应用
当你在一个类中使用 static 成员变量时,一定要问自己这个 static 成员变量需要考虑“线程安全吗?”(也就是说多个线程需要自己独立的 static 成员变量吗?)如果需要那就需要使用 ThreadLocal

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值