十、多线程基础-需要强化的知识点

2021-07-03 14:07

阅读:616

标签:new   获得   初始化   code   notifyall   模型   throw   线程优先级   learning   

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 ) {
                    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);
        }
    }
}
View Code

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关键字将解决线程之间可见性, 强制线程每次读取该值的时候都去“主内存”中取值
    */
}
View Code

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 ) {
            //等同于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();
        }
    }

}
View Code

8、volatile与synchronized区别
1)volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法
2)volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。使用Volatile不能保证线程的安全性(原子性)。synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。


十、多线程基础-需要强化的知识点

标签:new   获得   初始化   code   notifyall   模型   throw   线程优先级   learning   

原文地址:https://www.cnblogs.com/jiarui-zjb/p/9622773.html


评论


亲,登录后才可以留言!