首页 > 编程语言 > 详细

Java多线程笔记

时间:2020-06-14 18:55:57      阅读:61      评论:0      收藏:0      [点我收藏+]

这篇笔记是总结我看的《Java核心技术》并发一章。

要说线程,首先要说进程,操作系统运行程序的基本单位是进程,如今的操作系统早已是多任务操作系统,在同一时刻运行多个任务。操作系统将CPU的时间片分配每一个进程,给人并行处理的感觉。线程在低层次上扩展了进程,一个进程通常包含多个线程。区别在于进程拥有自己的一整套变量,线程则共享数据。线程之间的通信比进程更有效,容易。创建,销毁一个线程比进程开销小。

创建线程的方式

继承自Thread类

class MyThread extends Thread {

    @Override
    public void run() {
        super.run();
        //your code
    }
}

实现Runnable接口

Runnable myRunnable = () -> {
//your code };
new Thread(myRunnable).start();

中断线程

当线程的run方法执行完方法体最后一条语句后,或出现了在方法中无法捕获的异常时,线程将终止。

没有可以强制线程终止的方法,interrupt()方法可以用来请求终止线程。调用interrupt(),线程的中断状态被置位,中断状态是一个boolean标志。通过静态方法isInterrupted()方法来判断当前线程是否被中断。

当在一个被阻塞的线程(调用sleep()或者wait()方法)上调用interrupt()时,阻塞调用将会被Interrupted Exception异常中断

如果在中断状态被置位时调用sleep(),线程不会休眠,会清除这一状态,然后抛出InterruptedException异常。

interrupted()和isInterrupted()方法的区别

共同点:两个方法都用来检测当前线程是否被中断不同点:interrupted()是Thread的静态方法,在检测的同时会请求中断状态。isInterrupted()是实例方法,在检测时不会清除中断状态。

线程状态

New,用new创建一个新线程时,该线程还没有运行

Runnable,调用start(),线程处于runnable状态,可能正在运行,也可能没有在运行,取决于操作系统给线程提供运行的时间

Blocked,线程试图获取一个资源,而该资源被其他线程占用,该线程进入阻塞状态

Waiting,线程等待另一个线程通知调度器一个条件时,自己进入等待状态。

Timed waiting,给等待添加一个超时参数,等待状态保持到超时期满或接收到适当通知。

Terminated

线程终止因为如下两个原因:

run方法执行完自然退出

因为一个未捕获的异常终止了run方法

线程属性

线程优先级

每个线程都有一个优先级
默认情况下,继承自父类
调用setPriority()进行设置,从MAX_PRIORITY(10)到MIN_PRIORITY(1),NORM_PRIROTY为5
线程优先级高度依赖于系统,Java线程的优先级映射到宿主机平台的优先级上

守护线程

唯一用途是为其他线程提供服务
当只剩下守护线程时,虚拟机就退出

同步

多线程访问同一个资源会出现race condition(条件竞争),为防止出现并发访问的问题,代码块需要加锁

ReentrantLock
保护代码块的基本结构如下,保证了任何时刻只有一个线程执行代码块。只有一个线程可以获得锁,其他线程会被阻塞。
myLock.lock();

try {
    ...
} finally {
    myLock.unlock();
}

ReentrantLock是可重入锁,线程可以重复获得已持有的锁。ReentrantLock有一个hold count(持有计数)来跟踪lock()的嵌套调用。
线程每次调用lock()是都需要调用unlock()来释放锁

条件对象

线程进入并行代码块,却发现在某一条件满足下才能执行。这时需要条件对象,管理那些已经获得锁但不能做有用工作的线程。
一个锁对象可以有一个或多个相关的条件对象,通过以下方式来创建。
Condition condition = myLock.newCondition();
当线程发现条件不满足时,当前线程进入阻塞状态,并放弃锁,进入该条件对象的等待集,等待其他线程激活。
condition.await();
condition.sigal();
condition.sigalAll();
sigalAll()会激活因为这一条件而等待的所有线程,一旦成为可用的,将从await()返回,从被阻塞的地方继续执行。
如果没有其他线程来激活等待的线程,将导致死锁(deadlock)。

synchronized

从Java 1.0开始,每个Java对象都有一个内部锁,如果一个方法用synchronized声明,将获取对象的内部锁来保护该方法。
内部对象锁只有一个条件对象,wait()将线程添加到等待集中,notifyAll(),notify()解除线程的阻塞状态

wait() notifyAll() notify()是Object类的final方法,所以Condition对应的方法名改为await() signalAll() signal()
synchronized的另一个用法是同步代码块,线程进入是需要获取obj的内部锁
synchronized(obj) {
    ...
}

volatile关键字
如果一个字段被声明为volatile,那么编译器和虚拟机就知道该字段可能被另一个线程并发更新
volatile实现了可见性和

final
final修改的字段也可以被多线程安全的访问。其他线程只会在构造函数完成构造后才会看到变量的值。

原子性
java.util.concurrent.atomic包中很多类使用了高效的机器级指令来保证操作的原子性,不会有并发问题。
复杂的更新可以使用compareAndSet()方法,

乐观更新需要多次重试,性能会大幅下降
LongAdder
包括多个变量,可以有多个线程来更新加数,总和为当前值。
longAdder.add(x);
longAdder.sum();

LongAccumulator
LongAccumulator将这种思想扩展到其他操作上,在定义LongAccumulator时定义操作和零元素,get()获取当前值
LongAccumulator longAccumulator = new LongAccumulator(Math::max, 0);
longAccumulator.get();

Java多线程笔记

原文:https://www.cnblogs.com/minguo/p/13125718.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!