java基础知识学习--多线程

一、如何创建线程

1、继承Thread类

class CreateThread extends  Thread{
    @Override
    public void run() {
        System.out.println("启动线程");
    }
}

2、通过实现Runnable接口

class CreateThread1 implements Runnable{

    @Override
    public void run() {
        System.out.println("启动线程");
    }
}

3、通过Callable接口实现


class CtreateThread2 implements Callable{

    @Override
    public Object call() throws Exception {
        return null;
    }
}

4、通过线程池创建

 Executors.newFixedThreadPool(11);

二、继承Thread类或者实现Runable接口来创建线程,有什么区别

java只允许单继承,但允许调用多个接口,所以继承Thread具有单继承的局限性。
区别总结如下:
1、避免单继承造成的局限性
2、可以更好的体现共享

三、Thread类中start()和run()的区别

start()启动新线程,线程启动后会jvm调用run()方法,如果单独调用run方法,还是在当前线程中执行,不会创建新线程

四、 java 中Runnable和Callable的区别

Runnable和Callable都代表在不同的线程中执行任务目标Target,但是Callable可以返回结果和异常,但是Runnable不可以

五、线程中常用的方法

Thread.sleep(): 线程休眠,不会释放锁。
Thread.yield():当前线程让出cpu,但是并不能保证其他线程能够执行,有可能被当前线程再次获取cpu时间片
Thread.join():当前线程调用其他线程join()方法,当前线程进入等待状态,直至其他线程执行结束。当前线程等待状态被解除,进入就绪状态,或者阻塞状态。
Object.wait():调用当前对象扥带方法,释放当前对象锁,进入等待队列。
Object.notify()/Object.notifyAll():唤醒正在此对象监视器上等待的线程。

六、Java中volatile关键字

线程并发的三大特性:
可见性: 多个线程访问共享数据时,一个线程做了修改操作,其他线程立即知道
原子性: 一个或多个修改操作,要么都成功,要么都失败,不能中间终端
有序性: 程序的先后顺序按照代码的先后顺序执行(JVM允许编译器和处理器为了效率进行指令重排。单线程操作能够表现出串行语义,但是在多线程中此过程表现出无序性,因此在多线程开发过程中,需要注意指定重排对程序造成的影响,既要允许部分指令重排,又要保证有序性执行)

volatile 可见性

Java虚拟机中通过定义一套java内存模型,即通过屏蔽硬件和操作系统之间的差异,来保证java程序在各个平台能达到一致的并发效果,JMM的目标为:定义程序各个变量的访问规则,即在虚拟机中程序变量存储到内存和从内存中读取变量这样的细节。

JMM定义所有的变量都在主内存中,每个线程都有自己的工作内存,线程工作内存是主内存的副本,线程对变量的操作只能在自己的工作内存中操作,不能在主内存中操作,线程间的数据同步只能通过主内存完成同步。

volatile定义

1、当执行volatile执行写操作后,会将该变量最新值强制刷到主内存中
2、在执行写操作后,会将其他线程的该变量的缓存失效

所以volatile变量在读取变量时,发现缓存中的变量已失效,需要从主内存中读取,从而实现了可见性

volatile 防止指令重排

volatile防止指令重排是通过 内存屏障 来实现的。

硬件内存屏障

sfence:即写屏障,通过在写指令后面插入写屏障,能够使写入的缓存的数据回到主内存,保障其他线程的可见
lfence:即读屏障,在读之前插入屏障,能够在读之前让高速缓存中的数据失效,数据直接从主内存中读取
mfence:即智能屏障,集成sfence和lfence功能
lock前缀: lock前缀不是一种屏障,是一种锁,执行时会锁住内存子系统保证执行顺序,甚至可以跨多个CPU

JMM屏障

LoadLoad屏障: 对一Load1,LoadLoad,Load2,在Load2数据读取完成之前之前,保证Load1数据读取完毕;
LoadStore屏障:对Load1,LoadStoreStore1,在Store1写操作写入完成之前,保证Load数据读取完毕
StoreStore屏障:对Store1,StoreStore,Store2,在Store2写之前,保证Store1写操作完成,保证数据可见性
StoreLoad屏障:对Store1,Stored,Load1,在Load读取操作完成之前,保证Store1写操作完成,保证数据可见性

JVM在所有volatile读写操作之前均加上内存屏障,从一定程度上保证了执行有序性
LoadLoadBarrier
volatile读操作
LoadStoreBarrier


StoreStoreBarrier
volatile写操作
StoreLoadBarrier

六 CAS(CompareAndSwap)查找和替换

原理
预期值A和内存值V进行比较,如果一致,则将替换值B进行替换,如果不一致,不进行任何操作。
优点
1、cas是非阻塞的,可以避免死锁,线程之间的互相影响是非常小
2、没有锁竞争之间带来的系统开销,也没有线程间频繁调度的开销
缺点
1、ABA问题
解决方案:修改数据带版本,根据版本进行区分
2、cas只能对单个变量进行操作
解决方案:将多个变量放置一个对象进行操作
3、自旋带来的cpu开销
解决方案:降低自旋次数

六、Synchronized和ReentrantLock区别

Synchronized同步锁

组件
1、collectionList: 竞争队列
2、entryList: collectList中允许竞争锁资源的线程被移动到entryList
3:waitSet:那些调用wait()方法的线程放置在这里
4:onDeck:任意时刻,只允许有一个线程竞争当前锁资源,此线程被称为onDeck
5、owner:持有锁线程
6、!owner:释放锁线程
同步原理
1、JVM每次从队列尾部取出线程,成为onDeck线程,多线程情况下,collectionList会被大量的线程并发进行cas访问,为了降低队列尾部的竞争,collectionList将一部分数据移动至entryList用于候补竞争锁资源
2、 Owner线程解锁后,CollectList将一部分数据移动至EntryList,并指定entryList线程中的一个线程成为OnDeck(一般是entryList最先进入的一个)
3、Owner不将锁资源直接交给OnDeck,而是将竞争锁的资格传给OnDeck,
OnDeck需要重新竞争锁,这样虽然有失公平性,但是极大的增强了系统吞吐量
4、OnDeck线程竞争锁成功后,变成Owner线程,而没有获取到锁的线程继续留在EntryList中,当Owner线程调用wait方法,线程进入waitSet,直到唤醒,重新进入entryList进行锁竞争。
5、处于CollectionList,EntryList,WaitSet中的线程都是阻塞状态的,是由操作系统控制(linux通过pthread_mutex_lock内核函数执行)
6、synchronized是非公平锁,当线程进入collectionList之前,首先进行自旋获取锁,获取锁成功,直接成为Owner,这对已经进入队列的线程和OnDeck线程是不公平的
7、每个对象都有一个monitor对象,线程锁竞争就是在竞争monitor,代码块加锁通过monitorenter和monitorexit进行加锁和解锁(monitorexit分别在结束和异常位置插入进行解锁)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值