学习多线程你应该知道这些

三人行必有我师,人生是需要不断学习的,在这里我们相遇就是缘分,欢迎大家加群----四六零五七零八二四----让我们共同进步!希望各位可以看完这篇文章,也欢迎大家在下面留言讨论,觉得有帮助的也可以转载支持一下,谢谢大家!

        再说多线程之前我们先了解几个概念:

        线程:进程中负责程序执行的执行单元。一个进程中至少有一个线程。

        多线程:解决多任务同时执行的需求,合理使用CPU资源。多线程的运行是根据CPU切换完成,如何切换由CPU决定,因此多线程运行具有不确定性。

      线程池:基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

     使用多线程的优缺点:

     优点:

     1)适当的提高程序的执行效率(多个线程同时执行)。

     2)适当的提高了资源利用率(CPU、内存等)。

     缺点:

     1)占用一定的内存空间。

     2)线程越多CPU的调度开销越大。

     3)程序的复杂度会上升。

     对于多线程的示例代码感兴趣的可以自己写Demo啦,去运行体会,下面我主要列出一些多线程的技术点。

      synchronized

      同步块大家都比较熟悉,通过synchronized 关键字来实现;所有加上synchronized 的方法和块语句,在多线程访问的时候,同一时刻只能有一个线程能够访问。

        wait()、notify()、notifyAll()

        这三个方法是 java.lang.Object 的 final native 方法,任何继承 java.lang.Object 的类都有这三个方法。它们是Java语言提供的实现线程间阻塞和控制进程内调度的底层机制,平时我们会很少用到的。

      wait():

      导致线程进入等待状态,直到它被其他线程通过notify()或者notifyAll唤醒,该方法只能在同步方法中调用。

        notify():

        随机选择一个在该对象上调用wait方法的线程,解除其阻塞状态,该方法只能在同步方法或同步块内部调用。

      notifyAll():

        解除所有那些在该对象上调用wait方法的线程的阻塞状态,同样该方法只能在同步方法或同步块内部调用。

      调用这三个方法中任意一个,当前线程必须是锁的持有者,如果不是会抛出一个 IllegalMonitorStateException 异常。

        wait() 与 Thread.sleep(long time) 的区别

        sleep():在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),该线程不丢失任何监视器的所属权,sleep() 是 Thread 类专属的静态方法,针对一个特定的线程。

      wait() 方法使实体所处线程暂停执行,从而使对象进入等待状态,直到被 notify() 方法通知或者wait() 的等待的时间到。sleep()方法使持有的线程暂停运行,从而使线程进入休眠状态,直到用 interrupt 方法来打断他的休眠或者 sleep 的休眠的时间到。

      wait() 方法进入等待状态时会释放同步锁,而sleep() 方法不会释放同步锁。所以,当一个线程无限 sleep 时又没有任何人去interrupt 它的时候,程序就产生大麻烦了,notify()是用来通知线程,但在 notify() 之前线程是需要获得 lock 的。另个意思就是必须写在 synchronized(lockobj) {…} 之中。wait() 也是这个样子,一个线程需要释放某个 lock,也是在其获得lock 情况下才能够释放,所以wait() 也需要放在synchronized(lockobj) {…}之中。

        volatile 关键字

​       volatile 是一个特殊的修饰符,只有成员变量才能使用它。在Java并发程序缺少同步类的情况下,多线程对成员变量的操作对其它线程是透明的。volatile 变量可以保证下一个读取操作会在前一个写操作之后发生。线程都会直接从内存中读取该变量并且不缓存它。这就确保了线程读取到的变量是同内存中是一致的。

        ThreadLocal 变量

        ThreadLocal 是Java里一种特殊的变量。每个线程都有一个 ThreadLocal 就是每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了。如果为每个线程提供一个自己独有的变量拷贝,将大大提高效率。首先,通过复用减少了代价高昂的对象的创建个数。其次,你在没有使用高代价的同步或者不变性的情况下获得了线程安全。

        join() 方法

      join() 方法定义在 Thread 类中,所以调用者必须是一个线程,join() 方法主要是让调用该方法的 Thread 完成 run() 方法里面的东西后,再执行 join() 方法后面的代码,看下下面的”意思”代码:

      Thread t1 = new Thread(计数线程一);

      Thread t2 = new Thread(计数线程二);

        t1.start();

         t1.join(); // 等待计数线程一执行完成,再执行计数线程二

         t2.start();

         启动 t1 后,调用了join() 方法,直到 t1 的计数任务结束,才轮到 t2 启动,然后 t2 才开始计数任务,两个线程是按着严格的顺序来执行的。如果 t2 的执行需要依赖于 t1 中的完整数据的时候,这种方法就可以很好的确保两个线程的同步性。

     Thread.yield() 方法

      Thread.sleep(long time):线程暂时终止执行(睡眠)一定的时间。

      Thread.yield():线程放弃运行,将CPU的控制权让出。

      这两个方法都会将当前运行线程的CPU控制权让出来,但 sleep() 方法在指定的睡眠时间内一定不会再得到运行机会,直到它的睡眠时间完成;而 yield() 方法让出控制权后,还有可能马上被系统的调度机制选中来运行,比如,执行yield()方法的线程优先级高于其他的线程,那么这个线程即使执行了 yield() 方法也可能不能起到让出CPU控制权的效果,因为它让出控制权后,进入排队队列,调度机制将从等待运行的线程队列中选出一个等级最高的线程来运行,那么它又(很可能)被选中来运行。

欢迎关注技术公众号:架构师成长营

猜你喜欢

转载自blog.csdn.net/jiangbb8686/article/details/85011497