并发编程(五)-----多线程的常用操作方法(sleep,yield,join,interrupt)

1.线程的命名与取得

多线程的运行状态往往是不确定的,所以对于多线程操作必须有一个明确标识出线程对象的信息,这个信息就通过线程名称来描述,在Thread类中提供有如下取得线程名称的方法:

(1) 创建线程时设置名称的构造方法

public Thread (Runnable target,String name)

(2) 设置线程名称的普通方法

public final synchronized void setName(String name)

(3) 取得线程名称的普通方法

public final String getName() 

(4)取得当前线程对象

public static native Thread currentThread()

观察线程名称的取得

package Thread;
 
/**
 * Author:weiwei
 * description:2019/2/15
 * Creat:多线程名称的取得
 **/
 
class MyThread implements Runnable{
 
    @Override
    public void run() {
        for(int i=0;i<10;i++){
            System.out.println("当前线程: "+Thread.currentThread().getName()+",i="+i);
        }
    }
}
public class Thread1{
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        new Thread(mt).start();   //没有设置名字
        new Thread(mt).start();   //没有设置名字
        new Thread(mt,"weiwei").start();
    }
}

运行结果为:

当前线程: Thread-1,i=0
当前线程: Thread-1,i=1
当前线程: Thread-1,i=2
当前线程: Thread-1,i=3
当前线程: Thread-1,i=4
当前线程: Thread-1,i=5
当前线程: Thread-1,i=6
当前线程: Thread-1,i=7
当前线程: Thread-1,i=8
当前线程: Thread-1,i=9
当前线程: Thread-0,i=0
当前线程: Thread-0,i=1
当前线程: Thread-0,i=2
当前线程: Thread-0,i=3
当前线程: Thread-0,i=4
当前线程: Thread-0,i=5
当前线程: Thread-0,i=6
当前线程: Thread-0,i=7
当前线程: Thread-0,i=8
当前线程: Thread-0,i=9
当前线程: weiwei,i=0
当前线程: weiwei,i=1
当前线程: weiwei,i=2
当前线程: weiwei,i=3
当前线程: weiwei,i=4
当前线程: weiwei,i=5
当前线程: weiwei,i=6
当前线程: weiwei,i=7
当前线程: weiwei,i=8
当前线程: weiwei,i=9
 
Process finished with exit code 0

观察运行结果发现如果没有给线程设置名称的时候,系统会自动给该线程生成一个名字,需要注意的是,线程名字如果要设置要避免重复,同时中间不要修改.

观察线程的执行结果

package Thread;
 
/**
 * Author:weiwei
 * description:2019/2/16
 * Creat:多线程的执行结果
 **/
 
class MyThread implements Runnable {
 
    @Override
    public void run() {
        System.out.println("当前线程: " + Thread.currentThread().getName());
    }
}
 
public class Thread1{
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        mt.run();  //直接通过对象调用run()方法
        new Thread(mt).start(); //通过线程调用
    }
}

运行结果:

当前线程: main
当前线程: Thread-0
 
Process finished with exit code 0

通过运行结果发现,主方法main本身就是一个线程,所有线程都是通过主线程创建并启动的

2. 线程休眠(sleep方法)

线程休眠:指的是让线程暂缓执行一下,等到了预计时间后再恢复执行

状态变化:运行态---->阻塞态

线程休眠会交出CPU,让CPU去执行其他任务,但是要注意的是sleep方法 不会释放锁,但会立即交出CPU,也就是说当前线程如果持有某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象

休眠时间以毫秒为单位

public static native void sleep(long millis)throws InterruptedException

处理休眠操作

package Thread;
 
/**
 * Author:weiwei
 * description:2019/2/16
 * Creat:多线程的sleep方法
 **/
 
class MyThread implements Runnable{
 
    @Override
    public void run() {
        for(int i=0;i<1000;i++){
           try{
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
            System.out.println("当前线程: "+Thread.currentThread().getName()+",i="+i);
        }
    }
}
public class Thread1{
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}

虽然观察运行结果三个线程好像是同时休眠的,但是记住所有的代码都是依次进入到run()方法中的,进入代码的顺序可能有差异,但总体还是并发执行.

3. 线程让步(yield()方法)

线程让步:暂停正在执行的线程对象,并执行其他线程

状态变化:运行态---->就绪态

意思就是调用yield()方法会让当前线程交出CPU权限,让CPU去执行其他线程,它跟sleep()方法类似,同样不会释放锁,但是yield()方法不能控制具体的交出CPU的时间,另外yield()方法只能让拥有同样优先级的线程获得CPU执行时间得机会.

注意:调用yield()方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间这一点跟sleep()方法不同

观察yield()方法

package Thread;
 
/**
 * Author:weiwei
 * description:2019/2/16
 * Creat:多线程的yield方法
 **/
 
class MyThread implements Runnable{
 
    @Override
    public void run() {
        for(int i=0;i<3;i++){
            Thread.yield();
            System.out.println("当前线程: "+Thread.currentThread().getName()+",i="+i);
        }
    }
}
public class  Thread1{
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}

4. join()方法

join()方法:当前线程等待,另一线程执行完毕后再恢复执行

状态变化:运行态---->阻塞态

意思就是如果在主线程中调用join()方法就会让主线程休眠,让调用该方法的线程run方法先执行完毕之后再开始执行主线程

会释放对象锁

观察join()方法

package Thread;
 
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
 
/**
 * Author:weiwei
 * description:2019/2/16
 * Creat:多线程的join方法
 **/
 
class MyThread implements Runnable{
 
    @Override
    public void run() {
        try{
            System.out.println("主线程睡眠前时间");
            Thread1.printTime();
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName());
            System.out.println("睡眠结束时间");
            Thread1.printTime();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Thread1{
    public static void main(String[] args) throws InterruptedException {
        MyThread mt=new MyThread();
        Thread thread = new Thread(mt,"子线程A");
        thread.start();
        System.out.println(Thread.currentThread().getName());
        thread.join();
        System.out.println("代码结束");
    }
 
    public static void printTime() {
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time=format.format(date);
        System.out.println(time);
    }
}

运行结果:

main
主线程睡眠前时间
2019-02-16 20:13:02
子线程A
睡眠结束时间
2019-02-16 20:13:04
代码结束
 
Process finished with exit code 0

观察运行结果发现,调用了join()方法之后,主线程休眠了0.2s,然后再执行的子线程A

5. 线程停止

多线程中有三种方式可以停止线程。

  1. 设置标记位,可以是线程正常退出。
  2. 使用stop方法强制使线程退出,但是该方法不太安全所以已经被废弃了。
  3. 使用Thread类中的一个 interrupt() 可以中断线程。

设置标记位使线程退出

package Thread;
 
        
/**
 * Author:weiwei
 * description:2019/2/16
 * Creat:多线程的标记位使线程退出
 **/
 
class MyThread implements Runnable {
    private boolean flag;
 
    @Override
    public void run() {
        int i = 1;
        while (flag) {
            try {
                Thread.sleep(1000);
                System.out.println("第" + i + "次执行,线程名称为:" +
                        Thread.currentThread().getName());
                i++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public void setFlag(boolean flag){
        this.flag =flag;
    }
}
public class Thread1{
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread=new MyThread();
        Thread thread1=new Thread(myThread,"子线程A");
        thread1.start();
        Thread.sleep(2000);
        myThread.setFlag(false);
        System.out.println("代码结束");
    }
}

stop()方法使线程退出

package Thread;
 
 
/**
 * Author:weiwei
 * description:2019/2/16
 * Creat:stop方法退出多线程
 **/
 
class MyThread implements Runnable {
    private boolean flag;
 
    @Override
    public void run() {
        int i = 1;
        while (flag) {
            try {
                Thread.sleep(1000);
                System.out.println("第" + i + "次执行,线程名称为:" +
                        Thread.currentThread().getName());
                i++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public void setFlag(boolean flag){
        this.flag =flag;
    }
}
public class Thread1{
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread=new MyThread();
        Thread thread1=new Thread(myThread,"子线程A");
        thread1.start();
        Thread.sleep(3000);
        thread1.stop();
        System.out.println("代码结束");
    }
}

使用stop方法强制使线程退出,但是该方法不太安全所以已经被废弃了

为什么说不安全呢?因为stop会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对
象所运行的线程就会立即停止,假如一个线程正在执行:synchronized void { x = 3; y = 4;} 由于方法是同步的,
多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步
块中,它也会马上stop了,这样就产生了不完整的残废数据。

使用Thread.interrupt()

package Thread;
 
 
/**
 * Author:weiwei
 * description:2019/2/16
 * Creat:stop方法退出多线程
 **/
 
class MyThread implements Runnable {
    private boolean flag = true;
    @Override
    public void run() {
        int i = 1;
        while (flag) {
            try {
         /**
         * 这里阻塞之后,线程被调用了interrupte()方法,
         * 清除中断标志,就会抛出一个异常
         * java.lang.InterruptedException
         */
                Thread.sleep(1000);
                boolean bool = Thread.currentThread().isInterrupted();
                if (bool) {
                    System.out.println("非阻塞情况下执行该操作。。。线程状态" + bool);
                    break;
                }
                System.out.println("第"+i+"次执行,线程名称为:"+
                        Thread.currentThread().getName());
                i++;
            } catch (InterruptedException e) {
                System.out.println("退出了");
                       /**
                       * 这里退出阻塞状态,且中断标志被系统会自动清除,
                       * 并且重新设置为false,所以此处bool为false
                       */
                       boolean bool = Thread.currentThread().isInterrupted();
                       System.out.println(bool);  //退出run方法,中断进程
                       return;
            }
        }
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
public class Thread1 {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread, "子线程A");
        thread1.start();
        Thread.sleep(3000);
        thread1.interrupt();
        System.out.println("代码结束");
    }
}

运行结果:

第1次执行,线程名称为:子线程A
第2次执行,线程名称为:子线程A
代码结束
退出了
false

当第三次执行的时候调用interrupt方法,线程就退出了

interrupt() 方法只是改变中断状态而已,它不会中断一个正在运行的线程。这一方法实际完成的是,给受阻塞
的线程发出一个中断信号,这样受阻线程就得以退出阻塞的状态。

可以总结,调用线程类的interrupted方法,其本质只是设置该线程的中断标志,将中断标
志设置为true,并根据线程状态决定是否抛出异常。因此,通过interrupted方法真正实现线程的中断原理是:开
发人员根据中断标志的具体值,来决定如何退出线程。

6. 线程优先级

线程的优先级指的是,线程的优先级越高越有可能先执行,但仅仅是有可能而已。

在Thread类中提供有如下优先级方法:

(1)设置优先级

public final void setPriority(int newPriority)

(2)取得优先级

public final int getPriority()

对于优先级设置的内容可以通过Thread类的几个常量来决定

  1. 最高优先级:public final static int MAX_PRIORITY = 10;

  2. 中等优先级:public final static int NORM_PRIORITY = 5;

  3. 最低优先级:public final static int MIN_PRIORITY = 1;

设置优先级

package Thread;
 
 
/**
 * Author:weiwei
 * description:2019/2/16
 * Creat:stop设置线程优先级
 **/
 
class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5 ; i++) {
            System.out.println("当前线程:" + 
                    Thread.currentThread().getName()+" ,i = " +i);
        }
    }
}
public class Thread1 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        Thread t1 = new Thread(mt, "1");
        Thread t2 = new Thread(mt, "2");
        Thread t3 = new Thread(mt, "3");
        t1.setPriority(Thread.MIN_PRIORITY);
        t2.setPriority(Thread.MIN_PRIORITY);
        t3.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        t2.start();
        t3.start();
    }
}

运行结果:

当前线程:3 ,i = 0
当前线程:3 ,i = 1
当前线程:3 ,i = 2
当前线程:3 ,i = 3
当前线程:3 ,i = 4
当前线程:1 ,i = 0
当前线程:1 ,i = 1
当前线程:1 ,i = 2
当前线程:1 ,i = 3
当前线程:1 ,i = 4
当前线程:2 ,i = 0
当前线程:2 ,i = 1
当前线程:2 ,i = 2
当前线程:2 ,i = 3
当前线程:2 ,i = 4
 
Process finished with exit code 0

设置名称为1,2,3的线程,设置不同的优先级,从运行结果中得出,优先级分别由大到小为3,1,2.

主方法是一个线程,那么主线程的优先级是什么呢?

主方法只是一个中等优先级
线程具有继承性
线程是有继承关系的,比如当A线程中启动B线程,那么B和A的优先级将是一样的。

观察线程继承性

package Thread;
 
 
/**
 * Author:weiwei
 * description:2019/2/16
 * Creat:线程优先级
 **/
 
class A implements Runnable {
    @Override
    public void run() {
        System.out.println("A的优先级为:" + Thread.currentThread().getPriority());
        Thread thread = new Thread(new B());
        thread.start();
    }
}
class B implements Runnable {
    @Override
    public void run() {
        System.out.println("B的优先级为:" + Thread.currentThread().getPriority());
    }
}
public class Thread1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new A());
        thread.setPriority(Thread.MAX_PRIORITY);
        thread.start();
    }
}

运行结果:

A的优先级为:10
B的优先级为:10
 
Process finished with exit code 0

由于B继承了A,优先级都是10

7. 守护线程

守护线程是一种特殊的线程,它属于是一种陪伴线程。

简单点说 java 中有两种线程:用户线程和守护线程。可以通过 isDaemon() 方法来区别它们:如果返回 false ,则说明该线程是“用户线程”;否则就是“守护线程”。

创建的线程默认都是用户线程,包括主线程

典型的守护线程就是垃圾回收线程(GC)。

守护线程:后台线程,只有当当前JVM进程中最后一个用户线程终止,守护线程会随着JVM一同停止
注意:主线程main是用户线程。

setDeamn将当前用户线程置为守护线程

观察守护线程

package Thread;
 
 
/**
 * Author:weiwei
 * description:2019/2/16
 * Creat:守护线程
 **/
class A implements Runnable {
    private int i;
    @Override
    public void run() {
        try {
            while (true) {
                i++;
                System.out.println("线程名称:" + Thread.currentThread().getName() +
                        ",i=" + i + ",是否为守护线程:"
                        + Thread.currentThread().isDaemon());
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            System.out.println("线程名称:" + Thread.currentThread().getName() + "中断线程 了");
        }
    }
}
public class Thread1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new A(), "子线程A");
// 设置线程A为守护线程,此语句必须在start方法之前执行
        thread1.setDaemon(true);
        thread1.start();
        Thread thread2 = new Thread(new A(), "子线程B");
        thread2.start();
        Thread.sleep(3000);
// 中断非守护线程
        thread2.interrupt();
        Thread.sleep(10000);
        System.out.println("代码结束");
    }
}

从上面的代码可以看出来,B是用户线程当它中断了之后守护线程还没有结束,是因为主线程(用户线程)还没有
结束,所以说明是所有的用户线程结束之后守护线程才会结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值