其他分享
首页 > 其他分享> > 多线程二 基本技能

多线程二 基本技能

作者:互联网

前言

线程驱动任务,而我们需要的就是一种任务的描述,而这个描述由Runable接口来提供,想定义任务,只需要实现Runable接口并重写里面的run()就好

Thread的构造方法

方法名 描述
Thread() 创建新线程对象
Thread(String name) 创建新线程对象
Thread(Runnable target) 创建新线程对象
Thread(Runnable target, String name) 创建新线程对象,name为指定的线程名
Thread(ThreadGroup group, Runnable target) 分配新的 Thread 对象。
Thread(Runnable target) 创建新线程对象

Thread构造器通常需要一个Runable对象,我们把需要执行的任务放在run()中,程序运行后,会自动执行Runable对象的run()方法,以便在新的线程中执行我们指定的任务. start()方法是告诉cpu线程处于就绪状态

一. 继承Thread 创建线程


public class demo01 extends Thread {

    public  demo01(String name){
        super.setName(name);
    }

    public  demo01(){ }

    @Override
    public void run() {
        while(true)
        System.out.println(Thread.currentThread().getName()+"继承Thread类的线程执行了...");
    }

    public static void main(String[] args) {

        //new demo01()  执行Thread空参构造方法...
        demo01 d = new demo01();
        demo01 d2 =new demo01("helloThread");

        //线程就绪
        d.start();
        d2.start();

        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

二. 直接实现Runnable接口

实现runable接口 其实demo02 本类并不是一个Thread 他是一个线程任务


public class demo02  implements  Runnable{
    @Override
    public void run() {
       // while(true)
        System.out.println(Thread.currentThread().getName()+"线程执行了...");
    }

    public static void main(String[] args) {
       demo02  d = new demo02();
        Thread myThread = new  Thread(d);
        myThread.start();

        //lamaban表达式实现的run方法
        new Thread(()->{
            while(true)
            System.out.println("lamaban表达式实现的run方法执行了...");
        }
        ).start();

        //直接写
        new Thread(){
            @Override
            public void run() {
                System.out.println("hello Thread..");
            }
        }.start();

        //实现Runable() +  继承Thread();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Runnable接口里面的run");
            }
        }){
            //子类覆盖了 父类的run方法...
            @Override
           public void run(){
                System.out.println("Thread子类里面的run");
            }
        }.start();

    }

}

一般情况下,如果不是多继承,实现Runable接口和继承Thread类没啥区别

三. Callable 创建既有返回值,又可以抛出异常的线程

Callable同样可以理解成是一个任务的描述方式,只不过他不能直接丢给Thread,而是交给一个叫FutrueTask的容器包装,

Callable&Runable的区别

public class demo03  implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("hello Callable");
        //返回值的类型就是Callable接口的泛型
        return 1;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //未来的任务
        FutureTask<Integer> task = new FutureTask<>(new demo03());

        new Thread(task).start();

        System.out.println("返回值的结果是:"+task.get());
    }
}

刚才提到,Thread接收的任务是Runable类型的,现在FutureTask是个什么鬼?怎么把它传递进来的? 其实,FutrueTask实现了RunnableFuture接口,而这个接口继承了Runnable&Future,一切也就那么顺其自然了

当然我们知道,Runable里面的run()方法是由新new出来的线程异步执行的,那么现在重写的这个call()怎么个运行法?

查看FutrueTask的源码,我们可以看到,call()是由FetureTask的run()执行的,

 public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;

run()方法&call()方法

在往线程池中提交任务时 submit()方法同样可接受Callable对象,后续会详解

    Future<String> future = threadPoolExecutor.submit(new Callable<String>() {
                @Override
                public String call(){
                    ai.getAndIncrement();
                    return Thread.currentThread().getName();
                }
            });

四. Thread常用API

1 . interrupt() & isInterrupted()

interrupted():

它要配合 isInterrupted() ,作为条件,判断当前的状态,去中断, 本函数多次调用不会 改变 当前线程的状态

实例代码

停不下来的线程

/*
* 停止不了的线程
* */
public class CanNotStop extends Thread{

AtomicInteger value = new AtomicInteger(1);

@Override
public void run() {
    value.getAndIncrement();
    System.out.println("当前的value== "+value);
    /*
    * isInterrupted()  方法, 判断当前线程的状态去中断线程,
    * 假如说,当前线程被标记为要去中断, 被isInterrupted()限制的Target方法不再执行,条件之外的代码,能且只能执行一次! 然后彻底被中断
    * */
    while(!isInterrupted()) {

        try {
            sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "执行了..");
    }

    System.out.println("你真的以为我被中断了吗?");
}


    public static void main(String[] args) {

    //初始化
    CanNotStop canNotStop = new CanNotStop();
    CanNotStop canNotStop2 = new CanNotStop();



    canNotStop.start();
    canNotStop2.start();

    /*
    * 关于interrupt()方法,这个方法
    *         作用:
    *           中断当前线程,但是! 相比于废弃的stop() 并不是真正意义上的中断,而是打上了一个标记, 表示想要中断它.但是呢?在中断它之前要让他把该做的事,
    *     该跑的代码 跑完!
    *         特性:
    *            第一次执行interrupt()----> 标记当前线程是要被中断的
    *           第二次执行interrupt()---->  清除所有标记
    *
    *     它要配合 isInterrupted() ,作为条件,判断当前的状态,去中断, 本函数多次调用不会 改变 当前线程的状态
    * */

    canNotStop.interrupt();

    try {
        sleep(200);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    //被中断就相当于死掉了?
    synchronized (canNotStop){
        canNotStop.notifyAll();
    }

    }
}

这也称作是停止不了的线程,为啥这样说呢? 可以看到,执行 canNotStop.interrupt();代码的是谁? 没错主线程!细想一下,执行这个语句时,新创建的线程,本来就没有抢到CPU的执行权,也就是说他本来就是停止的... 他的原理就是打个标记,等到被打上标记的线程抢到CPU的执行权的时候,去判断一下就行了,如果存在被中断的标记,就什么事都不做,反之则执行任务.其实,它还活着

2 currentThread()

返回当前代码,正在被哪个线程调用

3 isAlive()

判断当前线程是否处于存活状态,线程处于正在运行或者等待运行的状态返回true,线程运行完毕返回false

4 sleep()

指定在线程休眠的时间,在指定的时间后,重新进入就绪状态去竞争CPU的执行权

5 getId() & getName()

返回线程的id & 名字

6 停止线程

throw new InterruptedException();
@Override
public void run(){
    while(true){
        if(this.isInterrupted()){
           return; 
        }
        System.out.println("if 后面的代码");
    }
}

推荐使用的是剖出异常的停止线程的方法,因为有了异常之后,可以在catch块中对异常进行处理,让程序更加流畅

7 yield()

8 暂停线程

五. 线程的优先级

线程的优先级用处是,把任务的重要性告诉调度器,让任务的调度器,更倾向于优先级高的线程先执行,但是也存在优先级底的线程先执行的可能

在<>提到,在绝大多数情况下,都应该使线程按照默认的优先级规则执行,试图操作优先级让线程先执行,通常是一种错误

此外,jdk中线程的优先级有是个,但是和操作系统都不能映射的很好,比如Windows系统是七个优先级,所以一般我们使用的是 MAX_PRIORITY NORM_PRIORITY MIN_PRIORITY

setPriority(int newPriority);

其中newPriority的值由1-10 ,若不在这个范围内,抛出IllegalArgumentExeception()

 getPriority();

特性:

守护线程(daemon)

设置为守护线程, 它肯定会随着主线程的退而退出
java线程中有两类,一类是用户线程(非守护线程) 一类是守护线程. 它的特性就是 伴随,去守护用户线程,
比如 java的GC(垃圾回收算法) 就是一个很称职的守护者!

setDaemon(true);
public class demo02 implements ThreadFactory {

    @Override
    public Thread newThread(Runnable r) {
        Thread thread = newThread(r);
        thread.setDaemon(true);
        return  thread;
    }
} 
 ExecutorService executorService = Executors.newCachedThreadPool(new demo02());

当再没有非守护线程后,守护线程中run方法中的finally代码块是不会执行而直接退出


参考书籍<>Bruce Eckel著 <> 高洪岩著

标签:run,Thread,基本技能,Callable,线程,new,多线程,public
来源: https://www.cnblogs.com/ZhuChangwu/p/11150281.html