三人行必有我师,人生是需要不断学习的,在这里我们相遇就是缘分,欢迎大家加群----四六零五七零八二四----让我们共同进步!希望各位可以看完这篇文章,也欢迎大家在下面留言讨论,觉得有帮助的也可以转载支持一下,谢谢大家!
在说多线程之前我们先了解几个概念:
线程:进程中负责程序执行的执行单元。一个进程中至少有一个线程。
多线程:解决多任务同时执行的需求,合理使用CPU资源。多线程的运行是根据CPU切换完成,如何切换由CPU决定,因此多线程运行具有不确定性。
![](https://img.haomeiwen.com/i7737491/d0bce48d879f344b.png)
线程池:基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
使用多线程的优缺点:
优点:
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关键字
![](https://img.haomeiwen.com/i7737491/323fbb1318fb1cf9.jpg)
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控制权的效果,因为它让出控制权后,进入排队队列,调度机制将从等待运行的线程队列中选出一个等级最高的线程来运行,那么它又(很可能)被选中来运行。
![](https://img.haomeiwen.com/i7737491/3e9748aa25b6c1de.jpg)
最后,如果想有一群“臭味相投”的朋友来一起交流学习的话,欢迎大家搜索群号:460570824,让我们共同进步!
网友评论