书接上回:多线程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()是主动终止