Java并发—线程常用方法总结
1.join()方法
join —— 让一个线程等待另一个线程完成才继续执行。如A线程线程执行体中调用B线程的join()方法,则A线程被阻塞,知道B线程执行完为止,A才能得以继续执行。
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
for(int i=0;i<5;i++){
System.out.println("main:"+Thread.currentThread().getName() + " " + i);
if(i==2){
thread.start();
thread.join();
}
}
}
}
运行结果:
main:main 0
main:main 1
main:main 2
Thread-0 0
Thread-0 1
Thread-0 2
Thread-0 3
Thread-0 4
main:main 3
main:main 4
2.Thread.sleep()方法
sleep 是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复,调用sleep 不会释放对象锁。由于没有释放对象锁,所以不能调用里面的同步方法。
public class Main {
private int j = 10;
Object obj = new Object(); public static void main(String[] args) throws InterruptedException {
Main main = new Main();
Runnable r1 = main.new MyRunnable();
Runnable r2 = main.new MyRunnable();
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
} class MyRunnable implements Runnable {
public void run() {
synchronized(obj){
for (int i = 0; i < 5; i++) {
j++;
System.out.println("线程:"+Thread.currentThread()+"i:"+i+",j:"+j+",time:"+ System.currentTimeMillis());
if(i==2){
try {
System.out.println("线程:"+Thread.currentThread()+"睡眠");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
运行结果:
线程:Thread[Thread-0,5,main]i:0,j:11,time:1503114191497
线程:Thread[Thread-0,5,main]i:1,j:12,time:1503114191497
线程:Thread[Thread-0,5,main]i:2,j:13,time:1503114191497
线程:Thread[Thread-0,5,main]睡眠
线程:Thread[Thread-0,5,main]i:3,j:14,time:1503114193500
线程:Thread[Thread-0,5,main]i:4,j:15,time:1503114193500
线程:Thread[Thread-1,5,main]i:0,j:16,time:1503114193500
线程:Thread[Thread-1,5,main]i:1,j:17,time:1503114193500
线程:Thread[Thread-1,5,main]i:2,j:18,time:1503114193501
线程:Thread[Thread-1,5,main]睡眠
线程:Thread[Thread-1,5,main]i:3,j:19,time:1503114195502
线程:Thread[Thread-1,5,main]i:4,j:20,time:1503114195502
从结果看,Thread-0休眠后没有释放锁,所以Thread-1并没有执行,直到Thread-0休眠结束并执行完毕后Thread-1才开始执行。
3.Thread.yield( )方法
调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。
public class Main {
public static void main(String[] args) throws InterruptedException {
Main main = new Main();
Thread t1 = main.new MyThread();
t1.start();
} class MyThread extends Thread{
public void run() {
long beginTime=System.currentTimeMillis();
int count=0;
for (int i=0;i<50000000;i++){
count=count+(i+1);
Thread.yield();
}
long endTime=System.currentTimeMillis();
System.out.println("用时:"+(endTime-beginTime)+" 毫秒!");
}
}
}
运行结果:
用时:4448 毫秒!
把Thread.yield();注释掉,运行结果:
用时:1 毫秒!
4.getPriority和setPriority
用来获取和设置线程优先级。
Thread t1 = new Thread();
t1.setPriority(Thread.MAX_PRIORITY);
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
5.setDaemon和isDaemon
用来设置线程是否成为守护线程和判断线程是否是守护线程。
Thread t1 = new Thread();
t1.setDaemon(true);
守护线程(后台线程)主要是为其他线程提供服务。如JVM中的垃圾回收线程。
守护线程的生命周期与前台线程生命周期有一定关联。主要体现在:当所有的前台线程都进入死亡状态时,后台线程会自动死亡。
6.wait()、notify()和notifyAll()
wait()、notify()和notifyAll()是Object类中的方法:
/**
* Wakes up a single thread that is waiting on this object's
* monitor. If any threads are waiting on this object, one of them
* is chosen to be awakened. The choice is arbitrary and occurs at
* the discretion of the implementation. A thread waits on an object's
* monitor by calling one of the wait methods
*/
public final native void notify(); /**
* Wakes up all threads that are waiting on this object's monitor. A
* thread waits on an object's monitor by calling one of the
* wait methods.
*/
public final native void notifyAll(); /**
* Causes the current thread to wait until either another thread invokes the
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object, or a
* specified amount of time has elapsed.
* <p>
* The current thread must own this object's monitor.
*/
public final native void wait(long timeout) throws InterruptedException;
从这三个方法的文字描述可以知道以下几点信息:
1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。
2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;
有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。
上面已经提到,如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁);
notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。
同样地,调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
nofityAll()方法能够唤醒所有正在等待该对象的monitor的线程,这一点与notify()方法是不同的。
这里要注意一点:notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。
举个简单的例子:假如有三个线程Thread1、Thread2和Thread3都在等待对象objectA的monitor,此时Thread4拥有对象objectA的monitor,当在Thread4中调用objectA.notify()方法之后,Thread1、Thread2和Thread3只有一个能被唤醒。注意,被唤醒不等于立刻就获取了objectA的monitor。假若在Thread4中调用objectA.notifyAll()方法,则Thread1、Thread2和Thread3三个线程都会被唤醒,至于哪个线程接下来能够获取到objectA的monitor就具体依赖于操作系统的调度了。
上面尤其要注意一点,一个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。
synchronized, wait, notify结合:典型场景生产者消费者问题:
/**
* 生产者生产出来的产品交给店员
*/
public synchronized void produce()
{
if(this.product >= MAX_PRODUCT)
{
try
{
wait();
System.out.println("产品已满,请稍候再生产");
}
catch(InterruptedException e)
{
e.printStackTrace();
}
return;
} this.product++;
System.out.println("生产者生产第" + this.product + "个产品.");
notifyAll(); //通知等待区的消费者可以取出产品了
} /**
* 消费者从店员取产品
*/
public synchronized void consume()
{
if(this.product <= MIN_PRODUCT)
{
try
{
wait();
System.out.println("缺货,稍候再取");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return;
} System.out.println("消费者取走了第" + this.product + "个产品.");
this.product--;
notifyAll(); //通知等待去的生产者可以生产产品了
}
参考:
Java并发—线程常用方法总结的更多相关文章
- Java 并发 线程同步
Java 并发 线程同步 @author ixenos 同步 1.异步线程本身包含了执行时需要的数据和方法,不需要外部提供的资源和方法,在执行时也不关心与其并发执行的其他线程的状态和行为 2.然而,大 ...
- Java 并发 线程的优先级
Java 并发 线程的优先级 @author ixenos 低优先级线程的执行时刻 1.在任意时刻,当有多个线程处于可运行状态时,运行系统总是挑选一个优先级最高的线程执行,只有当线程停止.退出或者由于 ...
- Java 并发 线程属性
Java 并发 线程属性 @author ixenos 线程优先级 1.每当线程调度器有机会选择新线程时,首先选择具有较高优先级的线程 2.默认情况下,一个线程继承它的父线程的优先级 当在一个运行的线 ...
- Java 并发 线程的生命周期
Java 并发 线程的生命周期 @author ixenos 线程的生命周期 线程状态: a) New 新建 b) Runnable 可运行 c) Running 运行 (调用 ...
- Java并发——线程安全、线程同步、线程通信
线程安全 进程间"共享"对象 多个“写”线程同时访问对象. 例:Timer实例的num成员,即add()方法是用的次数.即Timer实例是资源对象. class TestSync ...
- 从JDK源码角度看java并发线程的中断
线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止.在java中要让线程安全.快速.可靠 ...
- Java并发——线程介绍
前言: 互联网时代已经发展到了现在.从以前只考虑小流量到现在不得不去考虑高并发的问题.扯到了高并发的问题就要扯到线程的问题.你是否问过自己,你真正了解线程吗?还是你只知道一些其他博客里写的使用方法.下 ...
- java并发线程池---了解ThreadPoolExecutor就够了
总结:线程池的特点是,在线程的数量=corePoolSize后,仅任务队列满了之后,才会从任务队列中取出一个任务,然后构造一个新的线程,循环往复直到线程数量达到maximumPoolSize执行拒绝策 ...
- java 并发——线程
一.前言 前一篇文章总结了对 java 并发中的内置锁的理解,这篇文章来说说线程 ,并发与线程总有剪不断理还乱的关系.关于 java 线程的基本概念.线程与进程的关系以及如何创建线程,想必大家都很清楚 ...
随机推荐
- Nginx负载均衡和LVS负载均衡的比较分析(转)
Nginx负载均衡和LVS负载均衡的比较分析 作者:匿名 来源:ChinaZ源码报导 浏览:1032次 2011-12-6 15:12:27 字号:大 中 小 [摘要]Nginx是一个高性能的 HTT ...
- ubuntu中安装apache ab命令进行简单压力测试
1.安装ab命令 写道 apt-get install apache2-utils 2.ab命令参数说明. 写道 Usage: ab [options] [http[s]://]hostname[:p ...
- VUE入门实例,模版组件用法
这里每一个例子可以直接拷进body运行. 本系列为学习记录,并非大神教学案例. 仅仅整理用法,至于VUE的原理,设计模式等等暂不讨论,文中如有不对,还请大家帮忙指正,万分感激. 下一篇会写父子组件交互 ...
- Atitit. Toast alert loading js控件 atiToast v2新特性
Atitit. Toast alert loading js控件 atiToast v2新特性 1. 连续多个txt追加的原理 var txt = document.createElement(& ...
- jquery的param()
jQuery ajax - param() 方法 jQuery Ajax 参考手册 实例 序列化一个 key/value 对象: var params = { width:1900, height:1 ...
- jedis连接池的参数配置
<!-- 连接池的配置信息 --> <bean id="jedisConfig" class="redis.clients.jedis.Jedis ...
- hmtl表单
表单: <form id="" name="" method="post/get" action="负责处理的服务端&quo ...
- iOS 集成微信支付【转载】
目前项目里有微信支付的需求,调研过一段时间后,发现其实并没有想象中的那么困难.如果你只是想实现该功能,一个方法足以,但是若你想深入了解实现原理.就需要花费更多的功夫了.目前我只清楚微信支付需要做签名, ...
- php 使用curl 将文件上传
<?php /** * curl文件上传 * @var struing $r_file 上传文件的路劲和文件名 */ function upload_file($r_f ...
- TP中单字母快捷函数总结
ThinkPHP中有许多使用简便的单字母函数,可以很方便开发者快速的调用,但是字母函数却不方便记忆,本文将所有的字母函数总结一下,以方便以后查找.1.U() URL组装 支持不同URL模式 U($ur ...