Java 多线程

 线程间的任务不同,但是线程操作的数据相同
  有共享数据
  操作共享数据的代码超过一句

多线程:

  多线程的程序可以包括多个顺序执行流,多个顺序流之间互不干扰
  实现同时执行的效果,实际上是多个线程在争抢CPU
  可以实现不同功能的同时执行
  多线程不一定能够提高效率,但是可以合理的使用PCU资源
  多线程的运行结果是不同的,因为线程在争抢CPU,这就是多线程的随机性
  进程(Process):
    每个运行中的程序就是一个进程
    正在运行的程序,也就是在内存中开辟的内存空间

  线程(Thread):
    负责程序执行的一条执行路径,也称为一个执行单元
    进程的执行实际上是线程在执行
    一个进程至少会有一个线程(Main Thread),主线程执行main函数代码
    当一个进程中有多个线程时,就是多线程程序
    线程是随着任务的存在而存在,随着任务的结束而消失   任务(Task):     每个线程需要执行的代码     任务代码都有其存储位置     主线程的任务代码在main函数,垃圾回收线程的任务代码在finalize函数中
 JVM(Java 虚拟机)是不是多线程?

  至少有一个负责正常执行的线程,也就是执行main函数中的代码----主线程
   还有一个负责垃圾回收的线程,也就是执行finalize函数中的代码----垃圾回收线程

  JVM在后台提供了一个超级线程来执行垃圾回收
  每个对象都可以被回收,回收的功能定义在Object的finalize()方法中
  运行垃圾回收器,调用System的gc()方法

创建线程:

创建线程的第一种方式:
  
1 创建一个类继承Thread   2 重写Thread类中的run方法     创建线程是为了执行任务     任务代码必须有存储位置,run方法就是任务代码的存储位置   3 创建子类对象,实际在创建线程   4 启动线程     主线程的任务代码在main函数中     子线程的任务代码在run函数中 

run()方法只是一个普通的方法调用,不具备启动线程的功能
start()方法会启动线程并执行run()中的代码

创建线程的第二种方式:

  为了解决临界资源的问题,需要使用创建线程的第二种方式
  1 创建实现了Runnable接口的子类
  2 重写Runnable接口的run方法
  3 创建实现了Runnable接口的子类的对象
  4 创建Thread类的对象,也就是创建线程
  5 把实现了Runnable接口的子类对象作为参数传递给Thread的构造方法

优点:
  把线程任务进行了描述,也就是面向对象
  实现了线程任务和线程对象的分离,线程执行什么任务不再重要,只要实现了Runnable接口的子类对象都可以作为参数传递给Thread的构造方法
  实现了接口的同时还可以继承父类

创建线程的第三种方式:


  1 创建Callable接口的实现类

  2 重写Callable接口的call方法

  3 使用FutureTask类来包装Callable对象,该FutureTask对象封装了该call方法的返回值

  4 将FutureTask对象作为参数传递给Thread的构造方法

  5 使用FutureTask对象的get方法获得子线程结束后的返回值


总结: 

  通过继承Thread类和或者实现Runnable接口,Callable接口都可以实现多线程

  通过接口实现的两种方式相似,只是Callable接口定义的方法有返回值

 使用继承类和接口创建多线程的方式对比:

  Runnable和Callable线程类只是实现了Runnable和Callable接口,还可以继承其他类

  多个线程可以共享同一个对象,可以处理同一份资源

  访问当前线程必须使用Thread.currentThread方法

  Thread线程类已经继承了Thread,不能再次继承其他类

  访问当前线程只需this


为什么不能直接创建Thread类,然后调用start方法?

  任务代码必须写在run方法中,而Thread类中的run方法没有实现任何功能,代码执行没有结果
   所以只有继承Thread类,重写run方法

多线程内存:

  在栈中为主线程开辟内存
  多线程不遵循先进后出,每个线程在栈中都有内存,谁先获得CPU,就先执行
  当线程执行完自己的任务代码,线程就从栈中消失
  只有所有线程都结束,整个进程才结束

synchronized:

synchronized代码块的锁:obj
synchronized同步函数的锁:this
静态同步函数:
  静态函数进内存的时候不存在对象,但是存在其所属类的字节码文件,属于Class类型的对象,所以静态同步函数的锁是其所属类的字节码文件对象

避免死锁:
  容易发生在锁的嵌套

线程间的通信:   

线程间的任务不同,但是线程操作的数据相同
有共享数据
操作共享数据的代码超过一句

等待唤醒机制:      

必须用在同步中,因为同步中才有锁
指明让持有锁的线程去等待或被唤醒
等待的线程会放弃锁  
wait(),notify(),notifyAall()为什么定义在Object中?
   
   wait(),notify(),notifyAall()必须用在同步中,因为同步中才有锁
   锁是任意对象,任意对象都可以调用的方法需要定义在Obbject中 

jdk1.5 多线程的实现方式:

  jdk1.5之前:
    对锁的操作是隐式的
    synchronized(obj)//获取锁
    {

    }//释放锁
  jdk1.5之后:
    import.util.concurrent.Locks;
    有一个描述锁的Lock接口,锁被面向对象
    使用Lock的子类ReentrantLock创建一把锁

使用Lock替代同步代码块的方式:

使用Lock替代同步代码块的方式:
  1 创建一把锁
  2 把之前写在同步代码块中的代码写在lock()和unlock()之间
    lock()获取锁
    unlock()释放锁
    await()等待
      单独的定义在Condition接口中
      也被面向对象 
    signal()唤醒一个等待线程
    signalAll()唤醒所有等待线程
  3 得到一个和锁绑定的Condition对象

jdk1.5对唤醒等待方法也进行了单独的描述,描述的接口是Condition
唤醒等待方法必须结合着锁来使用,所以使用Lock的newCondition()方法来获得和锁绑定的对象

用Lock替代同步代码块之后出现了IllegaMonitorStateException(无效的监视器异常)?

 因为wait(),notify(),notifyAll()必须用在同步代码块中,而同步代码块被Lock替代掉了
 

守护线程:

垃圾回收线程就是一个守护线程
可以看成后台线程,依赖于前台线程
当前台线程全部结束时,即使守护线程的任务代码没有执行完也会立刻结束
在线程启动前设置启动线程

  setDaemon()设置为守护线程

  join()加入运行,主线程会让着此线程,等到此线程运行完主线程才会继续执行

线程池:

java.util.concurrent
  可以创建线程的对象
接口:ExeutorService

创建线程池:

  execute()开始执行任务

  创建只有一个线程的线程池
    任务只能一个一个执行,按照任务提交的先后顺序执行
    newSingleThreadExecutor()    
  创建固定个数的线程池
    任务数量和线程数量相同时,任务会同时执行
    任务数量大于线程数量时,多出来的任务会等待线程池中已有的线程空闲下来去执行
    newFixedThreadPool()
  创建缓冲的线程池
    自身会创建一定数量的线程
    线程数量大于任务数量,会自动销毁空闲的线程
    当任务增加时又会自动增加线程
    newCachedThreadPool()
  创建数量无限的线程池
    空闲的线程不会被销毁
    newScheduledThreadPool()

使用匿名内部类创建线程:

  new Thread()
  {
    public void run()
     {
  
     }
  }.start();

 

  new Thread(new Runnable()
  {
    public void run()
    {

    }
  }).start();

 


  Runnable r = new Runnable()
  {
    public void run()
    {

    }

  };
  new Thread(r).start();
原文地址:https://www.cnblogs.com/roxy/p/7290497.html