十多线程基础-需要强化的知识点
Posted jiarui-zjb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十多线程基础-需要强化的知识点相关的知识,希望对你有一定的参考价值。
1、sleep()和wait()方法异同
sleep方法和wait方法都可以用来放弃CPU一定的时间,不同点在于如果线程持有某个对象的监视器,sleep方法不会放弃这个对象的监视器,wait方法会放弃这个对象的监视器
1)Thread.sleep():方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。
2)Object.wait():线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态
2、start()和run()方法区别
start()方法:
1)用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。
2)通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到CPU时间片,就开始执行run()方法。
run()方法:
1)run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条。
总结:
1)调用start方法方可启动线程,
2)而run方法只是thread的一个普通方法调用,还是在主线程里执行。
3)把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用run()方法,这是由jvm的内存机制规定的。
4) 并且run()方法必须是public访问权限,返回值类型为void.
3、wait()和notify()/notifyAll()方法为什么要在同步块中被调用
这是JDK强制的,wait()方法和notify()/notifyAll()方法在调用前都必须先获得对象的锁
4、wait()和notify()/notifyAll()方法在放弃对象监视器时区别是什么
wait()方法立即释放对象监视器,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器 。
5、线程的join、yield、priority用法
1)thread1.join();可以将thread1理解为插队者,当thread1执行完毕之后当前线程才会继续执行
public class JoinThreadDemo02 { /* * T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行 * */ public static void main(String[] args) { final Thread t1 = new Thread(new Runnable() { public void run() { for (int i = 0; i < 20; i++) { System.out.println("t1,i:" + i); } } }); final Thread t2 = new Thread(new Runnable() { public void run() { try { t1.join(); } catch (Exception e) { // TODO: handle exception } for (int i = 0; i < 20; i++) { System.out.println("t2,i:" + i); } } }); Thread t3 = new Thread(new Runnable() { public void run() { try { t2.join(); } catch (Exception e) { // TODO: handle exception } for (int i = 0; i < 20; i++) { System.out.println("t3,i:" + i); } } }); t1.start(); t2.start(); t3.start(); } } package threadLearning.join_yield_priority; class JoinThread implements Runnable { public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "---i:" + i); } } }
2)现代操作系统基本采用时分的形式调度运行的线程,线程分配得到的时间片的多少决定了线程使用处理器资源的多少,也对应了线程优先级这个概念。在JAVA线程中,通过一个int priority来控制优先级,范围为1-10,其中10最高,默认值为5。下面是源码(基于1.8)中关于priority的一些量和方法。
t1.setPriority(10) // 注意设置了优先级, 不代表每次都一定会被执行。 只是CPU调度会优先分配
3)Thread.yield()方法的作用:暂停当前正在执行的线程,并执行其他线程。(可能没有效果)yield()让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。结论:大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
6、synchronized和ReentrantLock的区别
synchronized是和if、else、for、while一样的关键字,ReentrantLock是类,这是二者的本质区别。既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上:
1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁
2)ReentrantLock可以获取各种锁的信息
3)ReentrantLock可以灵活地实现多路通知
另外,二者的锁机制其实也是不一样的。ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word,这点我不能确定。
7、volatile关键字 (示列2个)
理解volatile关键字的作用的前提是要理解Java内存模型, volatile关键字的作用主要有两个:
1)多线程主要围绕可见性和原子性两个特性而展开,使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile变量,一定是最新的数据
2)代码底层执行不像我们看到的高级语言—-Java程序这么简单,它的执行是 Java代码–>字节码–>根据字节码执行对应的C/C++代码–>C/C++代码被编译成汇编语言–>和硬件电路交互 ,现实中,为了获取更好的性能JVM可能会对指令进行重排序,多线程下可能会出现一些意想不到的问题。使用volatile则会对禁止语义重排序,当然这也一定程度上降低了代码执行效率从实践角度而言,volatile的一个重要作用就是和CAS结合,保证了原子性,详细的可以参见java.util.concurrent.atomic包下的类,比如AtomicInteger。
普通变量在多线程中的不可见性示列:
/** * * @classDesc: 功能描述:Volatile 关键字的作用是变量在多个线程之间可见。 * 如果变量没有使用volatile关键字,那么变量在多线程之间是不可见的,线程读取的是该变量的副本,而不会从主内存中读取; * @author: zjb * @createTime: 创建时间:2018-6-24 下午4:57:55 * @version: v1.0 * @copyright: */ public class ThreadVolatileDemo extends Thread{ //public volatile boolean flag=true; public boolean flag=true; @Override public void run(){ System.out.println("开始执行子线程...."+Thread.currentThread().getName()); while (flag) { } System.out.println("线程停止"+Thread.currentThread().getName()); } public void setRuning(boolean flag) { this.flag = flag; } } package threadLearning.volatileKeyWord; public class ThreadVolatileDemoTest { public static void main(String[] args) throws InterruptedException { ThreadVolatileDemo threadVolatileDemo = new ThreadVolatileDemo(); threadVolatileDemo.start(); Thread.sleep(1000); threadVolatileDemo.setRuning(false); System.out.println("flag 已经设置成false"); Thread.sleep(1000); System.out.println("threadVolatileDemo.flag---》"+threadVolatileDemo.flag); } /* 已经将结果设置为fasle为什么?还一直在运行呢。 原因:线程之间是不可见的,读取的是副本,没有及时读取到主内存结果。 解决办法使用Volatile关键字将解决线程之间可见性, 强制线程每次读取该值的时候都去“主内存”中取值 */ }
AtomicInteger的原子性示例:
AtomicInteger :可以以原子方式更新的int值。
AtomicInteger用于诸如原子递增计数器之类的应用程序,不能用作java.lang.Integer的替换。但是,这个类确实扩展了Number,允许处理基于数字的类的工具和实用程序进行统一访问。
public class AtomicIntegerTest extends Thread { private static AtomicInteger atomicInteger = new AtomicInteger(); @Override public void run() { for (int i = 0; i < 100; i++) { //等同于i++ atomicInteger.incrementAndGet(); } System.out.println("atomicInteger----->"+atomicInteger); } public static void main(String[] args) { // 初始化10个线程 AtomicIntegerTest[] volatileNoAtomicThread = new AtomicIntegerTest[10]; for (int i = 0; i < 10; i++) { // 创建 volatileNoAtomicThread[i] = new AtomicIntegerTest(); } for (int i = 0; i < volatileNoAtomicThread.length; i++) { volatileNoAtomicThread[i].start(); } } }
8、volatile与synchronized区别
1)volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法
2)volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。使用Volatile不能保证线程的安全性(原子性)。synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。
以上是关于十多线程基础-需要强化的知识点的主要内容,如果未能解决你的问题,请参考以下文章