Java多线程的学习体会

Java多线程

1、创建线程的几种方式

继承 Thread 类

  1. 定义 Thread 类的子类,并重写该类的 run() 方法,该 run() 方法将作为线程执行体;
  2. 创建 Thread 子类的实例,即创建了线程对象;
  3. 调用线程对象的 start() 方法来启动该线程。

实现 Runnable 接口

  1. 定义 Runnable 接口的实现类,并实现该接口的 run() 方法,该 run() 方法将作为线性执行体;
  2. 创建 Runnable 实现类的实例,并将其作为 Thread 的 target 来创建 Thread 对象,Thread 对象作为线程对象;
  3. 调用线程对象的 start() 方法来启动该线程。

实现 Callable 接口

  1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,且该 call() 方法有返回值;
  2. 创建 Callable 实现类的实例;
  3. 使用 Future Task 类来包装 Callable 对象,该 Future Task 对象封装了该 Callable 对象的 call() 方法的返回值;
  4. 使用 Future Task 对象作为 Thread 对象的 target 创建并启动新线程。

各种线程创建方式的优缺点

使用 Runnable 和 Callable 接口创建多线程的优缺点:

  1. 可以继承其他的类;
  2. 多个线程可以共享一个 target 对象,适合多个相同线程来处理同一份资源的情况,可以将 CPU、代码和数据分开,体现面向对象的思想;
  3. 编程复杂,需要使用 Thread.currentThread() 方法。

使用继承 Thread 类的方式创建多线程的优缺点:

  1. 不能继承其他类;
  2. 编写简单,使用 this 即可获得当前进程。

2、Thread 的常用方法

Thread 类的常用构造方法

Thread();
Thread(String name);
Thread(Runnable target);
Thread(Runnable target, String name);
// 其中name为线程名,target为包含线程体的目标对象

Thread 类常用静态方法

currentThread();			// 返回当前正在执行的进程
interrupted();				// 返回当前执行的线程是否一定被中断
sleep(long millis);			// 使当前执行的线程睡眠一段时间
yield();					// 使当前执行的线程自愿暂时放弃对处理器的使用权并允许其他线程执行

Thread 类的常用实例方法

getId();						// 返回该线程id
getName();						// 返回线程名称
getPriority();					// 返回线程优先级
interrupt();					// 使该线程中断
isAlive();						// 判断该线程是否处于活动状态
isDaemon();						// 判断该线程是否为守护线程(线程默认为非守护)
setDaemon(boolean on);			// 将该线程标记为守护线程或用户线程
setName(String name);			// 设置线程名称
setPriority(int newPriority);	// 设置线程优先级
join();							// 等待该线程终止
join(long millis);				// 最多等待多长时间

启动子线程后,立即调用该线程的 join() 方法,则主线程会等待子线程执行完成后再执行。

3、线程的生命周期

新建(new)

  • 只能对新建状态的线程调用 start() 方法。

就绪(ready)

运行(running)

阻塞(blocked)

  1. 调用 sleep() 方法主动放弃占用的处理器资源;
  2. 调用了一个阻塞式IO方法;
  3. 试图获得一个同步监视器,但该监视器正被其他线程所持有;
  4. 线程在等待 notify;
  5. 调用 suspend() 方法将该线程挂起(容易导致死锁),可以用 resume() 方法恢复。

死亡(dead)

  1. run() 或 call() 方法执行完成;
  2. 线程抛出未捕获的 Exception 或 Error;
  3. 直接调用线程的 stop() 方法结束该线程(容易导致死锁)。

4、线程同步

  1. 同步方法:有 synchronized 关键字修饰的方法,由于 Java 每个对象都有一个内置锁,使用该关键字修饰方法,内置锁会保护整个方法,修饰静态方法则会锁住整个类;
  2. 同步代码块:有 synchronized 关键字修饰的语句块;
  3. ReentrantLock:
//创建锁对象
ReentrantLock lock = new ReentrantLock();
lock.lock(); //获取锁(锁定)
System.out.println("一段需要上锁的代码")
lock.unlock(); //锁释放
  1. volatile:比 synchronized 轻量,但不能保证原子性,每次使用该域会重新计算,而不使用寄存器中的值;
  2. 原子变量

5、多线程通信方式

  1. wait、notify、notifyAll(synchronized)
  2. await、signal、signalAll(lock)
  3. BlockingQueue

6、synchronized 和 Lock 的区别

  1. synchronized 是 Java 关键字,在 JVM 层面实现加锁和解锁;Lock 是一个接口,在代码层面实现加锁和解锁;
  2. synchronized 在代码执行完成或出现异常时会自动释放;Lock 不会自动释放,需要在 finally 中显式释放;
  3. synchronized 无法得知是否获取锁成功;Lock 则可以通过 tryLock 得知加锁是否成功。

7、乐观锁和悲观锁

悲观锁总是假设最坏的情况,每次拿数据的时候都会上锁,通过 synchronized 关键字或者 Lock 接口实现;

乐观锁在拿数据时不会上锁,而会在更新的时候判断在此期间别人有没有更新这个数据。

8、锁升级

锁的级别由低到高依次为:无锁、偏向锁、轻量级锁、重量级锁

9、线程池

线程池在系统启动时即创建大量空闲的线程,程序将一个Runnable对象或Callable对象传给线程池,线程池就会启动一个空闲的线程来执行他们的run()方法或call()方法,执行结束后该线程不会死亡,而是再次返回线程池中成为空闲状态,等待再次执行下一个Runnable或Callable的方法。

线程池的工作流程:

  1. 判断核心线程池是否已满,没满则创建一个新的工作线程来执行任务;
  2. 判断任务队列是否已满,没满则将新提交的任务添加到工作队列;
  3. 判断整个线程池是否已满,没满则创建一个新的工作线程来执行任务,已满则执行饱和策略。

线程池的工作状态:

  1. RUNNING:能接受新提交的任务,并且也能处理阻塞队列中的任务;
  2. SHUTDOWN:关闭状态,不在接受新提交的任务,但可以继续处理阻塞队列中已保存的任务,在RUNNING状态中调用shutdown()方法会使线程池进入该状态;
  3. STOP:不能接受新任务,也不处理队列中的任务,会中断正在处理中的任务,在RUNNING和SHUTDOWN状态中调用shutdownNow()方法会使线程池进入该状态;
  4. TIDYING:所有任务均已终止,workerCount为0,调用terminated()方法执行完后进入TERMINATED状态;
  5. TERMINATED:调用terminated()方法执行完后进入该状态。

线程池的拒绝策略:

  1. AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;
  2. DiscardPolicy:丢弃任务但不抛出异常;
  3. DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务;
  4. CallerRunsPolicy:由调用线程处理该任务。

线程池的队列大小设置:

  1. 对于CPU密集型任务:尽可能使用小的线程池,一般为CPU核心数+1,减少CPU过度切换;
  2. IO密集型任务:可以使用稍大的线程池,一般为CPU核心数*2,在等待IO时可以出去别的任务;
  3. 混合型任务:可以将任务分成IO密集型和CPU密集型,分别用不同的线程池去处理。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值