多线程2

书接上回:多线程1

java对thread的使用

方法三:使用匿名内部类创建:

public class Demo {
    Thread t = new Thread(){

    };
}

1.创建了一个Thread的子类,但是我们没有命名他的名字。

2.大括号里可以编写子类的定义代码,有哪些属性,代码,重写方法。

3.创建了这个匿名内部类的实例,并将他的值赋值给t

public class Demo {
    public static void main(String[] arge) {
        Thread t = new Thread() {
            public void run() {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        t.start();
        while(true){
            System.out.println("hello main");
        }
    }
}

这样写就可以少定义一些类,将不常用的类放在这。匿名内部类只能使用一次,一般只用于只是用一次的代码。

方法四:使用runnbale和匿名内部类

这类方法可以降低代码的耦合程度:

public class Demo {
    public static void main(String[] arge) throws InterruptedException {
        Runnable runnable = new Runnable(){

            @Override
            public void run() {
                while(true){
                    System.out.println("heloo thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                    }
                }
            }
        };
        Thread t  = new Thread(runnable);
        t.start();
        while(true){
            System.out.println("hello main");

                Thread.sleep(1000);
        }
    }
}

Thread不会直接执行任务,而是在实例化的()中传入任务。

这样做方便进行代码的修改,不会破坏太多逻辑。

方法五:引入ambda表达式。

ambda本质上是个匿名函数,最主要用途是作为”回调函数“。

在Java中,方法的实验必须依靠类来实现,所以java就出现一种接口:() -> {}

() -> {}相当于创建了一个匿名的函数式接口的子类,并且创造出对应的实体化还将里面的方法重写。

逻辑没变,只是编译器帮你做完了这些繁杂的步骤。

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            System.out.println("hello Thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t.start();
        while(true){
        System.out.println("hello main");
        Thread.sleep(1000);
        }
    }
}

与前面方法三方法四对比,这种写法会更加的简单。 

Thread其他的属性和方法

1.Thread() 最基础的用法,要重写run方法

2.Thread(Runnable target) 这种方法低耦合,不用重写Thread的方法

3.Thread(String name)和Thread(Runnable target,String name)在1和2的基础上给方法起个名字,方便使用者查找。

前台线程与后台线程

main主线程结束了但thread的线程不一定结束,他们还在运行导致程序还未停止。

public class Demo {
    static public void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while(true) {
                System.out.println("hello 1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"t1");
        t1.start();
        Thread t2 = new Thread(() -> {
            while(true) {
                System.out.println("hello 2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"t2");
        t2.start();
        Thread t3 = new Thread(() -> {
            while(true) {
                System.out.println("hello 3");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"t3");
        t3.start();
    }
}

主线程main在启动t1t,t2,t3后就结束运行了,但是t1,t2,t3线程却还在运行。这样能够影响到进程存在的线程我们称为前台线程

我们自己代码创建的线程和main线程都属于前台线程。       

可以通过 

而那种对线程无影响的线程则被称为后台线程。

package thread;

public class Demo {
        static public void main(String[] args) {
            Thread t1 = new Thread(() -> {
                while (true) {
                    System.out.println("hello 1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }, "t1");
            t1.setDaemon(true);
            t1.start();
            for (int i = 0; i < 3; i++) {
                System.out.println("hello main");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("main程结束");
        }
    }

   

setDaemon(true)可以将前台线程转换为后台进程,在这代码中无线循环的t1线程被设为了后台线程,不能控制进程的结束,随着main线程的结束而一起结束了。

Thread存活问题

进程之间存在父子关系,线程之间不存在父子关系 

java在创建Thread对象时,是和系统中的线程意义对应的,但是Thread的生命周期和系统中的线程的生命周期会不一致,以至于出现Thread对象还存活,但系统中的线程已经销毁。

package thread;

public class Demo {
    public static void main(String[] args){
        Thread t  =  new Thread(()->{
            for (int i = 0;i<3;i++){
                System.out.println("hello Threag");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);

                }
            }
        });
        t.start();
        while(true){
            System.out.println(t.isAlive());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

isAlive()适用于检查线程是否存货状态。

像这样,Thread的线程的方法在三秒后结束,系统中对应的线程也会销毁。但是Thread的对象t仍然存在。

启动一个线程——start

start是由Java标准库和/jvm提供的方法,本质上是调用操作系统API

每个Thread对象都只能start一次,每次想创建一个新的线程,都得创建一个新的Thread,不能重复利用。

java中Thread和操作系统的线程是一一对应的,有多少个Thread就最多有多少个start调用。

run是线程入口方法,不需要手动调用。

中断一个线程

1.让线程入口方法执行完毕,让润方法尽快结束。

public class Demo {
    private  static boolean isFinshed = false;//内部类访问外部类成员
    public  static void main(String [] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(!isFinshed ){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("线程结束");
        });
        t.start();
        Thread.sleep(3000);
        is = true;
    }
}

注意,在使用isFinshed 这个定义变量时候要注意定义范围,在lambda里面使用外面的变量,是会有问题的。

lambda是回调函数,他的执行时间可能会在很久以后,或许main函数都执行完了才开始执行它,如果isFinshed 定义在main函数里面,这样isFinshed 也随main函数一同销毁了。 

为了解决这个问题,java有个捕获机制,相当于拷贝了一份isFinshed 传给lambda,这样外面被销毁了也不影响它的执行。

为了解决这个问题,出现这个方法

package thread;
class Test{
    public int value = 0;
}

public class Demo {
    private  static boolean is = false;
    public  static void main(String [] args) throws InterruptedException {
        Test test = new Test(); //new Test()是对象本体,不会随着方法结束被销毁
        //等号前面则会随着方法结束而销毁
        Thread t = new Thread(()->{
            while(!is){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("线程结束");
        });
        t.start();
        Thread.sleep(3000);
        is = true;
    }
}

我们还可以使用Java自带的:

public class Demo11 {
    public  static void main(String [] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(Thread.currentThread().isInterrupted()){//判定线程是否被终止

            }
        });
        Thread.interrupted(); //主动终止
    }
}

isnterrupt()是主动终止

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值