Java 多线程 知识点整理

一、进程

  1. 进程:操作系统(OS)中正在执行的应用程序。
       允许多个进程同时工作
  2. 多进程并发执行原理:
       宏观上并行(一起执行)
       微观上串行(一个一个执行),获取CPU时间片的进程就会执行

二、线程

  1. 概念:在一个进程中,可以同时执行多个任务,每一个任务可以说是为一个线程,线程是进程的工作单位。线程也被称为轻量级的进程。

  2. 目前的程序是单线程:以main函数为开始,以main函数的结束为结束,这个线程为主线程。

  3. 线程的组成部分:
    (1)获取cpu时间片
    (2)栈空间独立,堆空间共享。
        每一个线程都有自己独有的栈空间
        所有线程共享同一堆空间
    (3)程序代码:利用Java代码实现多线程。

  4. 代码实现多线程的方式:【重点
    (1)第一种方式: Thread类
       a. 类 继承Thread类,同时覆盖 public void run( ) ,
       b. 创建线程
       c. 开启线程-> 利用start( )开启,jvm自动调用run()开启指定线程

public class TestThread {
	static{
		//创建线程
		MyThread mt = new MyThread();
		//开启线程
		mt.start();
	}
	
	public static void main(String[] args) {
		for (int i = 0; i < 15; i++) {
			System.out.println("+++++++ main +++++++"+i);
		}
	}
}
class MyThread extends Thread{
	public void run(){
		for (int i = 0; i < 10; i++) {
			System.out.println("------- run --------"+i);
		}
	}
}

结果1:                            结果2:
在这里插入图片描述           在这里插入图片描述

(2)第二种方式:Runnable接口
   a. 类实现 java.lang.Runnable接口,同时实现run(),即 public void run ( ) { }
   b. 创建目标对象
   c. 创建线程对象 Thread t = new Thread(Runnable target);
   d. 开启线程 t.start();

public class Class_2_Runnable {
	static{
		//创建目标对象
		Runnable r = new MyTarget();
		//创建线程对象
		Thread t = new Thread(r);
		//开启线程
		t.start();
	}
	public static void main(String[] args) {	
		for (int i = 0; i < 15; i++) {
			System.out.println(".........main........."+i);
		}
	}
}
class MyTarget implements Runnable{
	public void run(){
		for (int i = 0; i < 10; i++) {
			System.out.println("*******Runnable*******+i");
		}
	}
}

结果1:                         结果2:
在这里插入图片描述        在这里插入图片描述

(3)线程池:
  ①线程池:线程容器,将预先创建的线程对象存入到线程池中,只要将任务提交给线程池,会分配线程对象完成提交任务,线程池中的线程对象可重复使用。
  ②作用:避免频繁的创建和销毁线程,从而提高空间利用率呵执行效率。
  ③线程池常用的接口和类:(位于java.util.concurrent包中)
    a. Executor:线程池的顶级接口
    b. ExecutorService:是Executor的子接口,线程池的核心接口
      Ⅰ. submit(Runnable task):将线程任务提交给线程池
         submit(Callable<\T> task):将线程任务提交给线程池
      Ⅱ. shutdown():关闭线程池,将线程池中所有线程销毁(慎用)。
    c.Executors:获取线程池对象的工具类,其中方法基本都是静态方法
      Ⅰ. static ExecutorService newFixedThreadPool(int n):获取n个线程的线程池。
      Ⅱ. static ExecutorService newCachedThreadPool():获取动态数量线程对象的线程池,根据提交的任务需求,不够用自动完成线程创建。

(4)第四种方式:Callable接口
   a.位于 java.util.concurrent包中,类似于Runnable接口,对应的对象代表线程任务。注意Callable是泛型接口,泛型约束了接口中方法的返回值类型
   b.接口中的方法:V call();带有返回值的方法,同时可以抛出异常
                Future< T> f= pool.submit(c1);,使用 f.get() 从Future中获得所需的返回值

三、线程状态

  1. New状态 --> Runnable(Ready、Running) --> Terminated终止状态

  2. 线程状态 - 带有时间的等待状态
       a. static void sleep (long ms) : 如果在某一个线程中调用Thread.sleep() ,则此方法进入有期限等待状态,同时释放cpu时间片
       b. public final join():允许其他线程加入当前线程中并且优先执行,其结束后本身线程再执行

四、线程同步

多线程共同访问同一个对象,如果破坏了不可分割的操作,就可能导致数据不一致

临界资源:被多线程共同访问的对象
原子操作:不可分割的操作,要么都执行,要么都不执行

  1. 同步代码块
synchronized (obj) {}    对obj对象加锁的同步代码块
  1. Java中,每个对象都有一个互斥锁标记(monitor),用来分配给一个线程
  2. 只有拿到对象obj锁标记的线程,才能进入对obj加锁的同步代码块。
  3. 线程离开同步代码块时释放obj的锁标记

线程安全的对象:一个对象成为临界资源时,被多个线程访问,但其原子操作不会被破坏。

  1. 同步方法
public class A{
	public synchronized void m(){}
}

只有获得对象A的锁标记线程,才能调用A的同步方法
调用之前,竞争A的锁标记
方法调用结束后,会释放A的锁标记

  1. Lock
    a. Lock:接口,位于java.util.concurrent.locks包中,代表锁。
    b. Lock中常用方法
    (1)void lock() : 获取锁,如果被占用,则需要等待
    (2)void unlock() : 释放锁
       最好写在对应的try-finally中
    (3)实现类:ReentrantLock

五、死锁

  1. 死锁现象:两个线程或是多个线程相互占用对方所需资源,并都不释放,导致彼此相互等待对方释放资源,产生无限制等待的现象。
  2. 结果:出现死锁后,不会抛出异常,也没有任何提示,只是所有的线程都处于堵塞状态,如果没有外力介入,程序将无法继续。
  3. 解决方案:
    (1)尽可能调整加锁的方案
    (2)尽量避免同步的嵌套
    (3)采用线程中的通信

在Object类中游 wait() 和 notify()/notifyAll()
a. wait():
  ①让当前进程进入等待状态,释放cpu的同时,释放拥有的锁标记
  ②wait方法调用必须使用在调用它的对象所在的同步代码块中
   如 synchronized(o){
      o.wait();
     }
b. notify():
  ①notify(): 通知一个线程从等待状态结束
   notifyAll(): 通知所有线程从等待状态结束
  ②notify()/notifyAll()的调用必须在调用它的对象所在的同步代码块中,同wait②
  ③notify()和notifyAll() 只是其到通知的作用,不会释放锁标记

五、线程安全的集合 JDK5

解释:【面试题】
ArrayList:数组实现 JDK1.2 线程不安全 并发效率高
Vector: 数组实现 JDK1.0 线程安全  并发效率低

  1. ArrayList :所有方法都不是同步方法,当其成为临界资源时,可能会造成数据不一致,则线程不安全
  2. Vector :所有方法都是同步方法,成为临界资源时,不会造成数据不一致,则线程安全,并发效率很低

线程安全的集合类:既保证线程安全,有保证并发效率很高  【面试】

  1. CopyOnWriteArrayList
    (1)读操作不加锁,写操作加锁,并且在进行写操作时会copy一个旧集合,在新集合中写数据。
    (2)应用场景:读操作次数远大于写操作次数。效率仅次于ArrayList

  2. CopyOnWriteArraySet 原理和CopyOnWriteArrayList一致

  3. ConcurrentHashMap
    (1)JDK8以前,采用分段锁,控制锁的粒度,把Map分为16个片段,针对每个片段加锁。
    (2)JDK8以后,采用 CAS算法(比较交换算法),无锁算法,效率非常高,仅次于HashMap

  4. Queue ‘<'E> 接口
    (1)Queue是Collection的子接口,描述队列存储结构。简称FIFO
    (2)实现类 LinkedList
    (3)特点:先进先出,后进后出。
    (4)常用方法:boolean offer(Object o) 往队列中添加元素
                E poll() ,获取队列头元素

  5. BlockingQueue :
    (1)是Queue的子接口,位于java.util.concurrent包中
    (2)常用方法:put(Object o)往队列结构中存储元素,如果没有可用空间,则等待
                take():从队列结构中取元素,没有可取元素,则等待
    (3)常用实现类
    ArrayBlockingQueue:数组实现,指定固定容量,有界队列
    LinkedBlockingQueue:链表实现,可以实现无界队列

  6. ConcurrentLinkedQueue’<'E>
    (1)是Queue接口的实现类,位于java.util.concurrent包中(juc)
    (2)特点:无锁并且线程安全的集合实现类,采用CAS(比较交换算法)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值