十、多线程基础-需要强化的知识点
2021-07-03 14:07
标签:new 获得 初始化 code notifyall 模型 throw 线程优先级 learning 1、sleep()和wait()方法异同 2)现代操作系统基本采用时分的形式调度运行的线程,线程分配得到的时间片的多少决定了线程使用处理器资源的多少,也对应了线程优先级这个概念。在JAVA线程中,通过一个int priority来控制优先级,范围为1-10,其中10最高,默认值为5。下面是源码(基于1.8)中关于priority的一些量和方法。 AtomicInteger的原子性示例: AtomicInteger :可以以原子方式更新的int值。 8、volatile与synchronized区别 十、多线程基础-需要强化的知识点 标签:new 获得 初始化 code notifyall 模型 throw 线程优先级 learning 原文地址:https://www.cnblogs.com/jiarui-zjb/p/9622773.html
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 ) {
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 ) {
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 ) {
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 ) {
System.out.println(Thread.currentThread().getName() + "---i:" + i);
}
}
}
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用于诸如原子递增计数器之类的应用程序,不能用作java.lang.Integer的替换。但是,这个类确实扩展了Number,允许处理基于数字的类的工具和实用程序进行统一访问。public class AtomicIntegerTest extends Thread {
private static AtomicInteger atomicInteger = new AtomicInteger();
@Override
public void run() {
for (int i = 0; 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 ) {
// 创建
volatileNoAtomicThread[i] = new AtomicIntegerTest();
}
for (int i = 0; i ) {
volatileNoAtomicThread[i].start();
}
}
}
1)volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法
2)volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。使用Volatile不能保证线程的安全性(原子性)。synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。
上一篇:Python并发编程之协程
下一篇:线程池+协程+gevent模块