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并发—线程常用方法总结的更多相关文章

  1. Java 并发 线程同步

    Java 并发 线程同步 @author ixenos 同步 1.异步线程本身包含了执行时需要的数据和方法,不需要外部提供的资源和方法,在执行时也不关心与其并发执行的其他线程的状态和行为 2.然而,大 ...

  2. Java 并发 线程的优先级

    Java 并发 线程的优先级 @author ixenos 低优先级线程的执行时刻 1.在任意时刻,当有多个线程处于可运行状态时,运行系统总是挑选一个优先级最高的线程执行,只有当线程停止.退出或者由于 ...

  3. Java 并发 线程属性

    Java 并发 线程属性 @author ixenos 线程优先级 1.每当线程调度器有机会选择新线程时,首先选择具有较高优先级的线程 2.默认情况下,一个线程继承它的父线程的优先级 当在一个运行的线 ...

  4. Java 并发 线程的生命周期

    Java 并发 线程的生命周期 @author ixenos 线程的生命周期 线程状态: a)     New 新建 b)     Runnable 可运行 c)     Running 运行 (调用 ...

  5. Java并发——线程安全、线程同步、线程通信

    线程安全 进程间"共享"对象 多个“写”线程同时访问对象. 例:Timer实例的num成员,即add()方法是用的次数.即Timer实例是资源对象. class TestSync ...

  6. 从JDK源码角度看java并发线程的中断

    线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止.在java中要让线程安全.快速.可靠 ...

  7. Java并发——线程介绍

    前言: 互联网时代已经发展到了现在.从以前只考虑小流量到现在不得不去考虑高并发的问题.扯到了高并发的问题就要扯到线程的问题.你是否问过自己,你真正了解线程吗?还是你只知道一些其他博客里写的使用方法.下 ...

  8. java并发线程池---了解ThreadPoolExecutor就够了

    总结:线程池的特点是,在线程的数量=corePoolSize后,仅任务队列满了之后,才会从任务队列中取出一个任务,然后构造一个新的线程,循环往复直到线程数量达到maximumPoolSize执行拒绝策 ...

  9. java 并发——线程

    一.前言 前一篇文章总结了对 java 并发中的内置锁的理解,这篇文章来说说线程 ,并发与线程总有剪不断理还乱的关系.关于 java 线程的基本概念.线程与进程的关系以及如何创建线程,想必大家都很清楚 ...

随机推荐

  1. atitit.atiOrmStoreService 框架的原理与设计 part1  概述与新特性

    atitit.atiOrmStoreService 框架的原理与设计 part1  概述与新特性 1. 新特性如下 支持生成sql在无数据库连接的情况下 2. Orm设计 主要的俩个以来service ...

  2. strex,ldrex

    volatile bool lock = false; void func(void) { int i; while(lock);   lock = true; for(i = 0; i < 4 ...

  3. redhat5.8系统学习

    # redhat5.8系统学习 ### 简介-----------------------------redhat操作系统是红帽公司的收费版操作系统 ### 查看系统版本号-------------- ...

  4. scp命令的用法

    用法: scp 命令 scp 能够在 2个 linux 主机间拷贝文件: 命令基本格式: scp [可选參数] file_source file_target ====== 从 本地 拷贝到 远程 拷 ...

  5. Play on Words UVA - 10129 欧拉路径

    关于欧拉回路和欧拉路径 定义:欧拉回路:每条边恰好只走一次,并能回到出发点的路径欧拉路径:经过每一条边一次,但是不要求回到起始点 ①首先看欧拉回路存在性的判定: 一.无向图每个顶点的度数都是偶数,则存 ...

  6. VMware虚拟机 Ubuntu 实用技巧 (2)桥接模式连接网络与网卡的配置

    1.先用ifconfig查看当前的网卡配置,一般没有进行设置之前,打印的信息如下所示. ens33 Link encap:以太网 硬件地址 02:0c:29:c6:be:c7 inet6 地址: fe ...

  7. 操作XML-dom4j

    首先是到dom4j的官网dom4j文件包,下载之后解压如下所示. 在根目录中,找到dom4j-1.6.1jar包,加入到eclipse中的lib文件下,最后build path一下,即可使用相关的方法 ...

  8. python3----函数(join)

    今天写python 100例时,有个题目是大致是这样的:已知输入形式是1+3+2+1,要求输出形式为1+1+2+3 一开始思路是将输入的字符串用split()函数划分成数组,在对数组进行排序,再用fo ...

  9. Linux命令之paste

    介绍 cut用来从文本文件或标准输出中抽取数据列或者域,然后再用paste可以将这些数据粘贴起来形成相关文件.粘贴两个不同来源的数据时,首先需将其分类,并确保两个文件行数相同. paste将按行将不同 ...

  10. ios应用,64位问题,Missing 64-bit support

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYmFyb25fYmxvZ3M=/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...