Java多线程笔记总结

来源:互联网 发布:linux 修改文件名 编辑:程序博客网 时间:2024/05/16 18:16

1.线程的三种创建方式

参考之前的总结: 多线程-创建线程的三种方式

对比三种方式:

  1. 通过继承Thread类实现
  2. 通过实现Runnable接口
  3. 实现Callable接口

第1种方式无法继承其他类,第2,3种可以继承其他类;

第2,3种方式多线程可以共享同一个target对象,多个线程处理同一个资源;

一般使用第2,3种方式创建线程。

2.线程的生命周期

1.新建(new) 2.就绪(start) 3.运行(获得cpu资源) 4.阻塞(sleep,IO阻塞等)4.死亡(执行完成,Exception/Error)
这里写图片描述

3.线程常用方法

  1. join() :Thread对象调用,线程A调用join(),其他线程被阻塞,直到线程A执行完为止。

  2. setDaemon(true) : Thread对象调用,设置成后台线程; 当所有前台线程都死亡,后台线程自动死亡。

  3. sleep(): 静态方法,让正在执行的线程暂停,进入阻塞状态。
  4. yield(): 静态方法,让正在进行的线程暂停,进入就绪状态,系统的线程调度器重新调度一次;只有优先级比当前调用yield()的线程高或相同,并且处于就绪状态的线程才能获得执行。
  5. setPriority(int newPriority), getPriority(): 设置和获取线程的优先级;newPriority范围1-10;Thread三个静态常量,MAX_PRIORITY, 10; NORM_PRIORITY, 5; MIN_PRIORITY, 1;子线程和父线程具有相同的优先级,优先级高的线程比优先级低的线程执行机会更多。

4.线程同步

多个线程访问同一个数据时(线程调度的不确定性),很容易出现线程安全问题。解决办法:引入同步监视器,任意时刻只能有一个线程获得对同步监视器的锁定,当同步代码块执行结束后,该线程释放对该同步监视器的锁定。

“加锁”—>”修改共享资源”->”释放锁”

同步代码块

// obj就是同步监视器synchronized(obj){  同步代码块}

同步方法

使用synchronized修饰某个方法,则该方法称为同步方法,同步方法的同步监视器是this;

只需对会改变竞争资源的方法进行同步。

任何线程在进入同步代码块或同步方法前,必须获得同步监视器的锁定。
释放同步监视器的锁定的时机:

  1. 当前线程的同步代码块,同步方法执行结束
  2. 当前线程在同步代码块,同步方法中遇到break,return终止了执行
  3. 当前线程在同步代码块,同步方法中出现了未处理的Error或Exception
  4. 执行了同步监视器对象的wait()方法

同步锁(Lock)

Lock对共享资源的独占访问,每次只能一个线程对Lock对象加锁。
Lock,ReadWriteLock接口,对应的实现类ReentrantLock(可重入锁),ReentrantReadWriteLock。

class A{    private final ReentrantLock lock = ReentrantLock();    public void method(){        // 加锁        lock.lock();        try{            // 修改共享资源        }finally{            // 释放锁            lock.unlock();        }    }}

死锁

两个线程互相等待对方释放同步监视器,就发生了死锁。

线程池

在系统启动时,创建大量空闲的线程,将一个Runnable对象或Callable对象传给线程池,线程池会启动一个线程执行对应的run()或call(),当run()或call()执行完成后,该线程返回到线程池成为空闲状态,等待下个Runnable对象或Callable对象。

创建线程池

Executors工厂类,提供如下静态方法创建不同的线程池:

// 具有缓存功能的线程池,系统根据需要创建线程,这些线程会被缓存在线程池中newCachedThreadPool()// 固定数量,可重用的线程池newFixedThreadPool(int nThreads)// 单线程的线程池newSingleThreadExecutor()// 指定线程数量,并可指定延迟时间才执行线程任务newScheduleThreadPool(int corePoolSize)// 单线程,并可指定延迟时间才执行线程任务newSingleThreadScheduleExecutor()

前三个静态方法return ExecutorService
后两个静态方法return ScheduledExecutorService

ExecutorService代表线程池,提供3个方法:

// 将一个Runnable对象提交给线程池,Future返回nullFuture<?> submit(Runnable task)// 将一个Runnable对象提交给线程池,Future返回指定结果resultFuture<T> submit(Runnable task, T result)// 将一个Callable对象提交给线程池,Future返回Callable对象中call()方法Future<T> submit(Callable<T> task)

步骤:

  1. Executors静态工厂类创建ExecutorService
  2. 创建Runnable或Callable对象,作为线程执行体
  3. 调用ExecutorService实例的submit()方法提交Runnable或Callable
  4. 线程池关闭,调用ExecutorService实例的shutdown()方法

ThreadLocal

线程局部变量,把数据放在ThreadLocal中,就可以为每个线程创建一个该变量的副本,避免并发访问的线程安全问题;

private ThreadLocal<T> threadLocal = new ThreadLocal<>();// 当前线程副本中的值T get();// 删除void remove();// 设置当前线程副本中的值void set(T value);

与同步机制的区别:

同步机制是为了同步多个线程对共享资源的并发访问,是多线程间通信的有限方式;
ThreadLocal隔离多个线程的数据共享,避免多个线程间对共享资源的竞争,不需要对多个线程进行同步。

线程安全集合

通过Collections包装成线程安全集合:

ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap等都是线程不安全;如果多个线程对这些集合读,写时,会破坏这些集合数据的完整性。

Collections提供静态方法将这些集合包装成线程安全的集合。

synchronizedCollection(Collection<T> c)synchronizedList<List<T> list>synchronizedMap(Map<K, V> map)synchronizedSet(Set<T> set)
HashMap m = Collections.synchronizedMap(new HashMap);

线程安全集合:

ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet, ConcurrentLinkedQueue, ConcurrentLinkedDeque

CopyOnWriteArrayList
CopyOnWriteArraySet

0 0