首页 > 技术文章 > 多线程

macro-renzhansheng 2020-03-24 09:17 原文

基础概念

多线程:
     进程:一个程序中至少一个进程,一个进程至少一个线程。
    
     用户线程,正常情况下创建
     守护线程:需要手动创建
    
     线程是通过调用start()方法准备启动
    
     线程调度!
    
线程的创建方式:两种,
     1.Thread类派生,覆盖run()
     2.实现Runnable接口

线程调度

image

什么是线程调度?线程是不可控的,那么如何干预线程的执行?
     -等待:
         让某一个线程进入等待状态,等到条件满足后,被其他线程唤醒
         等待需要在同步环境,未持锁则报错
         等待后被唤醒,需重新经过持锁状态,然后回到就绪状态,等待资源分配
     -休眠:
         是让当前线程进入休眠状态,这是Thread的静态方法,休眠只能自己响
         1.至少消耗指定时间,(等待资源分配)
         2.休眠不可以被唤醒,休眠必须有参数!
        
     -让步:
         1.不建议使用优先级、礼让!因为最终结果不可控!
         2.使用,先设优先级,再启动线程
         3.让步是不可控的
         4.没有经过阻塞,直接回到就绪状态
        
     -合并线程:
         1.是让并行线程,变为串行状态
         2.先执行,再合并
         3.什么情况下要合并线程?
             某个线程要等到另外一个线程结果时,需要合并,已等待前面的结果。
        
     -守护线程:(用户线程的区别--所有用户线程执行完毕后,其他所有守护线程killed)
         1.当用户线程全部执行完毕后,守护线程立即停止,无论他执行到哪儿
         2.守护线程,先设置,再执行。
         应用情况:定时任务;清理日志;定时同步数据

    
    
     -唤醒:

一个对象可以持有多个锁吗?所以:线程之间争夺锁资源唯一

线程同步
     1.同步(底层看不到)
         同步方法:保护整个方法当中所有数据的安全
         同步代码块:保护方法当中某个区域的数据安全,意义在于让那些不需要考虑到同步的数据先执行,以提高运行效率。
         一般来说:属于线程本身数据区域的数据和  读的数据
     2.非静态同步,同步的锁是对象锁,只有相同实例才存在同步
     3.静态同步,锁的是类,只要是这个类,就会触发同步。
     4.不建议大家混杂同时使用静态和非静态锁。
    
     容器基本都是多线程!

内部封装好的安全类:

ByteArrayInputStream---FilterInputStream
StringBuffer
Collections

Note:

--即使是线程安全的类,也应该特别注意,因为操作的线程之间仍然不一定安全。
     线程安全类的安全仅仅局限于:它本身,但是其他操作不一定!

基本概念:

死锁:--解决,按照预定义的顺序获取锁。
     1.两个线程互相持有对方的锁,且互相等待,这种情况下,有小概率发生死锁。
     2.避免死锁的策略,就是按照相同的顺序获取锁,并释放锁。

原子化:
     一个整体、数据完整
     --要么都成功,要么都失败。


wait()\notify()\notifyAll()三个方法:

看下面的代码,这个代码执行的话会报错,java.lang.IllegalMonitorStateException

上网查了一下,明白了。

1>当前线程不含有当前对象的锁资源的时候,调用obj.wait()方法;
2>当前线程不含有当前对象的锁资源的时候,调用obj.notify()方法。
3>当前线程不含有当前对象的锁资源的时候,调用obj.notifyAll()方法。


生产消费者模型

生产消费者模型:
     1.消息队列,服务发布和服务消费。。。。
     消费者:
         1.先来后到的顺序
         2.消费者需要的东西,多种多样
         3.消费者需要的东西,可以不需要一次性全部获取
         4.如果生产者的能力达到上限,那么需要等待。
     生产者:
         1.生产者需要尽可能按照消费的先后顺序完成生产
         2.生产者还必须保证所有的生产都要完成
         3.如果没有消费者的消费需求,则需要等待。

//定义队列对象(+泛型)

import java.util.LinkedList;

public class MyQueue<T> {
     private LinkedList<T> queue;
     private final int DEFAULT_CAPACITY = 20;
     private int maxSize;
     public MyQueue() {
         this.maxSize = DEFAULT_CAPACITY;
         this.queue = new LinkedList<T>();
     }
     public MyQueue(int size) {
         this();
         this.maxSize = size;
         this.queue = new LinkedList<T>();
     }
     public synchronized void put(T t) {
         if (queue.size()>=this.maxSize) {
             System.out.println("队列已满...等待");
             try {
                 this.wait();
             } catch (InterruptedException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
         }
         queue.add(t);
         //唤醒的是当前对象wait()的线程
         this.notifyAll();
     }
     public synchronized T get() {
         if (queue.size()<=0) {
             System.out.println("队列为空...等待");
             try {
                 this.wait();
             } catch (InterruptedException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
         }
         this.notifyAll();
         return queue.removeLast();
     }
    
     public synchronized int getSize() {
         return this.queue.size();
     }
}

//生产者、消费者持有同一个线程对象

//生产线程

public class ProducerThread implements Runnable{
     private final MyQueue q;
     private final String message;
     public ProducerThread(final MyQueue q,final String message){
         this.q = q;
         this.message = message;
     }
     @Override
     public void run() {
         //每隔2秒生产一个
         while(true){
             try {
                 Thread.sleep(500);
                 q.put(Thread.currentThread()+":"+message);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     }
  

//消费者线程

public class ConsumerThread implements Runnable{
     private final MyQueue q;
     public ConsumerThread(final MyQueue q){
         this.q = q;
     }
     @Override
     public void run() {
         // TODO Auto-generated method stub
         while(true){
             try {
                 Thread.sleep(1000);
                 Object obj = q.get();//消费
                 System.out.println(obj);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     }
}

//测试


public class Test {

    public static void main(String[] args) {
         // TODO Auto-generated method stub
         MyQueue q = new MyQueue();
         //多生产者
         new Thread(new ProducerThread(q, "生产者1")).start();
         new Thread(new ProducerThread(q, "生产者2")).start();
         new Thread(new ProducerThread(q, "生产者3")).start();
        
         //多消费
         new Thread(new ConsumerThread(q)).start();
         new Thread(new ConsumerThread(q)).start();
         new Thread(new ConsumerThread(q)).start();
     }

}


Volatile:关键字,不建议使用!

??

有限的情形下使用volatile变量替代锁。
     1.对变量的写操作不依赖于当前值。




final型的变量是禁止修改的,也就不存在线程安全的问题。

//继承自父类的属性,this.属性指向super.属性,都是指向父类的同一个属性(效果等效)


反射的应用

--Java.lang.reflect.*
应用:
     1.反射调用方法———将方法作为对象,而不再是类名
     2.field.setAccessible(true);--暴力更改访问机制,访问private
     3.constructor.newInstance()

面向对象编程
     开发期间:类是构成Java程序的单位,Java程序通过类来组织其结构
     运行期间:对象是Java程序在运行时的单位,运行期间的Java程序是通过多个对象之间的相互作用关系来完成需求,动态。
    
null:空指针,内存地址的一个值,指空,不存在。
垃圾回收机制GC:是一个独立的巡查线程,GC是一个定时任务,隔一段时间执行一次。

基本数据类型在栈内存中开辟空间,同时直接将具体数值(而不是地址)保存在栈中
     --int numb = null;报错
引用类型在栈中开辟空间,同时在堆中开辟空间,创建对象,将对象地址保存在栈中。
内存模型,原理?



推荐阅读