多线程的常用操作方法

线程的命名与取得

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

NO.方法名称类型描述
1public Thread(Runnable target,String name)构造创建线程的时候设置名称
2public final synchronized void setName(String name)普通设置线程名称
3public final String getName()普通取得线程名字

要想取得线程的对象,在Thread类中提供有一个方法取得当前线程对象:

/**
     * Returns a reference to the currently executing thread object.
     *
     * @return  the currently executing thread.
     */
    public static native Thread currentThread();

观察线程名称取得

package ThreadMethod;
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 NameTest {
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        new Thread(mt).start();//没有设置名字
        new Thread(mt).start();//没有设置名字
        new Thread(mt,"lele").start();
    }
}

我们的运行结果是:

当前线程Thread-0,i=0
当前线程lele,i=0
当前线程Thread-1,i=0
当前线程lele,i=1
当前线程Thread-0,i=1
当前线程lele,i=2
当前线程Thread-1,i=1
当前线程lele,i=3
当前线程Thread-0,i=2
当前线程Thread-0,i=3
当前线程Thread-0,i=4
当前线程Thread-0,i=5
当前线程lele,i=4
当前线程lele,i=5
当前线程lele,i=6
当前线程lele,i=7
当前线程lele,i=8
当前线程lele,i=9
当前线程Thread-1,i=2
当前线程Thread-0,i=6
当前线程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-0,i=7
当前线程Thread-0,i=8
当前线程Thread-0,i=9
当前线程Thread-1,i=9

Process finished with exit code 0

我们通过上面的代码会发现,如果没有设置线程名字,则会自动分配一个线程名字。需要注意的是,线程名字如果要设置请避免重复,同时中间不要修改
观察线程的执行结果:

package ThreadMethod;
/*
* 这是我们观察我们的线程运送过程的代码*/
class MyThread1 implements Runnable{
    @Override
    public void run() {
        System.out.println("当前线程"+Thread.currentThread().getName());
    }
}
public class runTest {
    public static void main(String[] args) {
        MyThread1 mt=new MyThread1();
        mt.run();//直接通过对象调用run()方法
        new Thread(mt).start();//通过线程调用
    }
}

我们的运行结果:

当前线程main
当前线程Thread-0

Process finished with exit code 0

通过以上的程序我们发现,主方法本身就是一个线程,所有线程都是通过主线程创建并且启动的。
我们的问题是:我们的进程在哪里?
实际上每当使用了java命令去解释程序的时候,都表示启动了一个新的JVM进程。而主方法只是这个进程上的一个线程而已。
在讲述线程的其他常用方法时,我们先来看下面的图:

线程休眠(sleep方法)

线程休眠:指的是让线程暂缓执行一下,等到了预计时间之后再恢复执行。
线程休眠会交出CPU,让CPU去执行其他任务。但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象
方法:

public static native void sleep(long millis)throws InterruptedException

休眠时间使用毫秒作为单位
范例:处理休眠操作

package ThreadMethod;
class MyThread2 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 sleepTest {
    public static void main(String[] args) {
        MyThread2 mt=new MyThread2();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}

通过代码运行观察,我们会错误的认为这三个线程是同时休眠的,但是千万要记住,所有的代码是一次进入到run()方法中的。真正进入到方法中的对象可能是多个,也可能是一个,进入代码的顺序可能有差异,但是总体的执行是并发执行的。

线程让步(yield()方法)

暂停当前正在执行的线程第项,并执行其他线程。
意思就是调用yield方法会让当前线程交出CPU权限,让CPU去执行其他线程。他跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
注意:调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。
观察yield方法

package ThreadMethod;
class MyThread3 implements Runnable{
    @Override
    public void run() {
for(int i=0;i<10;i++){
    Thread.yield();
    System.out.println("当前线程"+Thread.currentThread().getName()+"i="+i);
}
    }
}
public class yieldTest {
    public static void main(String[] args) {
        MyThread3 mt=new MyThread3();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}

其实,我们多线程,用我们的眼睛观察是观察不出来什么的。

join()方法

等待该线程终止。意思就是如果在主线程中调用该方法时就会让主线程休眠,让调用该方法的线程run方法先执行完毕之后再开始执行主线程。

package ThreadMethod;

import MyCallable.Test;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

class MyThread4 implements Runnable{
    @Override
    public void run() {
        System.out.println("主线程睡眠前的时间");
        joinTest.printTime();
        try {
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName());
            System.out.println("睡眠时间结束");
            joinTest.printTime();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class joinTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread4 mt=new MyThread4();
        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-04-09 19:47:44
子线程A
睡眠时间结束
2019-04-09 19:47:46
代码结束

Process finished with exit code 0

线程停止

多线程中有三种方法可以停止线程
1、设置标记位,可以使线程正常退出
2、使用stop方法强制使线程退出,但是该方法不安全,已经被放弃了。
3、使用Thread类中的一个interrupt()可以中断线程。

设置标记位使线程退出
package OutThread;

/*
*
* 这是我们的代码里面设置标记位是我们的程序退出的方法
* */
class MyThread implements Runnable{
    private boolean flag=true;
    @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 flageTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread=new MyThread();
        Thread thread1=new Thread(myThread,"子线程");
        thread1.start();
        Thread.sleep(2000);
        myThread.setFlag(false);
        System.out.println("代码结束");
    }
}

运行结果:

1次执行,线程名称为子线程
代码结束
第2次执行,线程名称为子线程
使用stop方法使线程退出
package OutThread;

class MyThread1 implements Runnable{
    @Override
    public void run() {
        int i=1;
        while(true){
            try {
                Thread.sleep(1000);
                System.out.println("第"+i+"次执行,线程名称为:"+Thread.currentThread().getName());
                i++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class stopTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread1 myThread1=new MyThread1();
        Thread thread1=new Thread(myThread1,"子线程");
        thread1.start();
        Thread.sleep(2000);
        thread1.stop();
        System.out.println("代码结束");
    }
}

运行结果:

1次执行,线程名称为:子线程
第2次执行,线程名称为:子线程
代码结束

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

使用Thread.interrupt()
package OutThread;

class MyThread2 implements Runnable{
    private boolean flag=true;
    @Override
    public void run() {
        int i=1;
        while(flag){
            try {
                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,所以此处boolean为false
                * */
                boolean bool=Thread.currentThread().isInterrupted();
                System.out.println(bool);
                //退出run方法,中断进程
                return;
            }
        }
    }
    public void setFlag(boolean flag){
        this.flag=flag;
    }
}
public class interruptTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread2 myThread2=new MyThread2();
        Thread thread=new Thread(myThread2,"子线程");
        thread.start();
        Thread.sleep(3000);
        thread.interrupt();
        System.out.println("代码结束");
    }
}

运行结果:

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

interrupt()方法自适改变中断状态而已,它不会中断一个正在运行的线程。这一实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程就得以退出阻塞的状态。
然而:interrupte()并不会立即执行中断操作;具体而言,这个方法只会给线程设置一个为true的中断标志(中断标志只是一个布尔类型的变量),而设置之后,则根据线程当前的状态进行不同的后续操作。如果,线程的当前状态处于非阻塞状态,那么仅仅是线程的中断标志被修改为true而已;如果线程的当前状态处于阻塞状态,那么将中断标志设置为ture后,还会有如下三种情况之一的操作:
如果是wait,sleep以及join三个方法引起的阻塞,那么会将线程的中断标志重新设置成为false,并抛出一个InterruptedException;
如果在中断时,线程处于非阻塞状态,则将中断标志修改为true,而在此基础上,一旦进入阻塞状态,则按照阻塞状态的情况来处理;例如,一个线程在运行状态中,其中断标志被设置为true之后,一旦线程被调用wait、join、sleep方法中的一个,立马抛出一个InterruptionException,且中断标志被程序会自动清楚,重新设置为false。
通过上面的分析,我们可以总结,调用线程类的interrupted方法,其本质只是设置该线程的中断标志,将中断标志设置为true,并根据线程状态决定是否抛出异常。因此,通过interrupted方法真正实现线程的中断原理是:开发人员根据中断标志的具体值,来决定如何退出线程。

线程优先级

线程优先级指的是,线程优先级越高越有可能先被执行,但仅仅是有可能而已。
在Thread类中提供有下面的优先级方法:
设置优先级

public final void setPriority(int newPriority)

取得优先级

public final int getPrority()

对于优先级设置的内容可以通过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 proiroty;

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 Test {
    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(10);
        t2.setPriority(5);
        t3.setPriority(1);
        t1.start();
        t2.start();
        t3.start();
    }
}

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

public class Test{
Public static void main(String[] args){
System.out.println(Thread.currentThread.getPriority());
}
}

我们会发现,我们的主线程只是一个中等优先级的线程
线程具有继承性
线程是有继承关系的,比如当A线程中启动B线程,那么B和A的优先级将是一样的。
观察线程继承性:

package proiroty;
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 extendProiroty {
    public static void main(String[] args) {
        Thread thread=new Thread(new A());
        thread.setPriority(Thread.MAX_PRIORITY);
        thread.start();
    }
}

我们的运行结果为:

A的线程优先级为:10
B的优先级为:10

守护线程

守护线程是一种特殊的线程,它属于一种陪伴线程。简单点说就是java中有两种线程:用户线程和守护线程。可以通过isDaemon()方法来区别它们:如果返回false,则说明该用户是“用户线程”,否则就是“守护线程”。典型的守护线程就是我们的垃圾回收线程。只有当前JVM进行中存在任何一个非守护线程没有结束,守护线程就在工作;只有当最后一个非守护线程结束时,守护线程才会随着JVM一同停止工作。
注意:我们的主线程main时用户线程
观察守护线程:

package Keep;
class A implements Runnable{
    private int i;
    @Override
    public void run() {
        while(true){
            i++;
            System.out.println("线程名称"+Thread.currentThread().getName()
            +",i="+i+",是否为守护线程:"+Thread.currentThread().isDaemon());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("线程名称"+Thread.currentThread().getName()+"中断线程了");
            }
        }
    }
}
public class keepThread {
    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是用户线程,当它中断后守护线程还没哟u结束,是因为主线程(用户线程)还没有结束,所以说明是所有的用户线程及术后守护线程才会结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Android中,有多种方法可以创建和管理多线程。以下是几种常用的方法: 1. 使用Thread类:可以通过创建Thread对象并重写run()方法来创建线程。例如: ```java Thread thread = new Thread(new Runnable() { @Override public void run() { // 在此处执行耗操作或其他任务 } }); thread.start(); // 启动线程 ``` 2. 使用AsyncTask类:AsyncTask是Android提供的一个简化多线程编程的工具类,它在后台执行任务,并在主线程中更新UI。例如: ```java private class MyTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... params) { // 在此处执行耗操作或其他任务 return "Task Completed"; } @Override protected void onPostExecute(String result) { // 在主线程中处理任务完成后的操作,例如更新UI } } // 创建并执行AsyncTask MyTask task = new MyTask(); task.execute(); ``` 3. 使用Handler和Looper:Handler和Looper结合使用可以实现线程间的消息传递和处理。通过创建Handler对象,您可以在子线程中发送消息到主线程,并在主线程的Handler中处理消息。例如: ```java private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // 在主线程中处理消息 } }; // 在子线程中发送消息到主线程 Thread thread = new Thread(new Runnable() { @Override public void run() { // 在子线程中执行耗操作或其他任务 Message message = handler.obtainMessage(); // 设置消息内容 handler.sendMessage(message); } }); thread.start(); // 启动线程 ``` 这些方法只是Android多线程编程的一些常用方式。根据您的需求和场景,您可以选择适合您应用程序的方法。同,请注意在多线程编程中要注意线程安全和避免UI操作在非主线程进行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值