java 多线程之TWO

参考博客

田守枝Java技术博客

java程序启动至少会启动几个线程?

public class Main {

	public static void main(String[] args) throws InterruptedException {
		ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
		ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
		for(ThreadInfo threadInfo:threadInfos) {
			System.out.println(threadInfo.getThreadId()+"-"+threadInfo.getThreadName());
		}
	}

}

至少是5个:这是在JDK1.8中运行的结果

5-Attach Listener
4-Signal Dispatcher //分发处理发给JVM信号的线程
3-Finalizer //垃圾回收的线程
2-Reference Handler //清除reference的线程
1-main   //主线程

什么是当前线程?

当前在运行的线程称之为当前线程(Current Thread),但当前线程不是固定的。

我们可以使用Thread的currentThread()来获取当前线程的信息。

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

多线程的优点:

资源利用率更好    程序响应更快    程序设计在某方面边的简单

多线程的代价

设计更复杂    上下文切换的开销    增加资源消耗(线程运行的时候需要从计算机里获取一些资源)

JVM划分给每个线程的内存区域称之为线程栈内存。默认情况下,栈内存的大小1M。也就是说,你每多启动一个线程,至少要多消耗1M的内存资源。

Java创建线程运行时代码的三种方式:

继承Thread类 重写run方法

实现Runnable接口,实现run方法

实现Callable接口,实现call方法。jdk1.5中引进的

但创建线程只有一种方式 创建Thread对象的实例

创建子类还是实现Runnable接口?

对于这两种方式哪种好并没有一个确定的答案,它们都能满足要求。最好实现Runnable接口这种方法。因为Java中有一个线程池的概念。所谓线程池,可以理解为有一堆线程对象已经创建好了,那么其缺的就是线程运行时代码。所以我们只需要提供了运行时代码就好了,因此实现Runable接口可能是更好的一种方式。

想要让创建的新线程执行run()方法,必须调用新线程的start方法。

尽管启动线程的顺序是有序的,但是执行的顺序并非是有序的

Jvm和操作系统一起决定了线程的执行顺序,他和线程的启动顺序并非一定是一致的。

线程名

当创建一个线程的时候,如果我们不给线程明确的起一个名字的话,JVM默认会给其指定一个默认的名字。

主线程的名字是就是main,这是固定的。

JVM自动给线程命名的策略:如果没有明确的线程指定名字,JVM就会以Thread为前缀,按照线程创建的顺序,给其命名

获取当前正在执行的线程的名称有两种方式:如果运行时代码是直接覆盖Thread类的run方法编写的,可以通过this.getName()的方式获取线程的名字;如果只是实现了Runable接口,可以通过Thread.currentThread().getName()的方式获取。

Thread对象run方法与start()方法的区别

start()方法用于启动线程,run方法用于执行线程的运行代码,通过start()方法启动线程,然后JVM会回调线程的运行时代码

当我们new出一个Thread对象的时候,其仅仅只是Java中的一个对象而已。只有当我们调用了start方法的时候,其才会真正的成为一个运行的线程,也只有当线程启动后,JVM才会给线程分配相应的资源,例如栈内存空间。

而run方法中的只是线程的运行时代码而已,也就是这个线程要干的事。

覆盖了Thread对象的run方法,同时传入一个Runnable的实现类(实现了其run方法),会执行哪个?   

public class Main {

	public static void main(String[] args) {
		new Thread(new Thread2()) {
			public void run() {
				System.out.println("Thread ...");
			}
		}.start();
	}
    
}


public class Thread2 implements Runnable {

    
	@Override
	public void run() {
		System.out.println("Runnable ...");
	}	
}


结果是 执行Thread的run方法

 private Runnable target;

@Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

默认的run方法的逻辑是,如果传递了Runnable的实现类,那么运行Runnable的run方法

但是,在这个案例中,我们重写了这个run方法,所以这段逻辑根本不存在,运行run方法是,实际上运行的是我们覆盖后的方法中的内容。

线程的休眠

线程对象一旦调用其start方法之后,就会运行,运行的就是run方法中的代码,等到run方法中的代码运行结束,线程就执行完成。

如果我们想在线程运行的时候,让其暂停一会,可以调用线程sleep方法,sleep方法的作用是让线程进行休眠,意思是线程暂停执行。在执行的休眠时间内,该线程是不会请求CPU进行执行,因此其他线程可以获得更多的运行机会。

注意sleep方法抛出了一个InterruptedException异常。

线程的中断

中断(interrupt)表示一个线程应该停止当前所做的事而去另外一件事。通常中断是一个线程给另外一个线程发送中断信号,程序员自行决定如如何进行响应,也就是说收到中断信号后,接下来该做什么。通常情况下,线程收到中断信号后,采取的操作都是停止运行。

public class Main {

	public static void main(String[] args) throws InterruptedException {
		Thread t = new Thread() {
			@Override
			public void run() {
				for(int i = 0;i<10;i++) {
					try {
					    Thread.sleep(4000);
					    System.out.println("自定义线程:当前时间 "+new Date().toLocaleString());
					}catch (InterruptedException e) {
						e.printStackTrace();
						System.out.println("自定义线程:收到中断信号,总共循环了"+i+"次");
					}
				}
			}
		};
		t.start();
		Thread.sleep(12000);
		System.out.println("主线程:等待12秒发送信号");
		t.interrupt();
	}
 
}

这样有一个问题 中断后会继续运行,所以要加一个 在catch里要加上return;

这也间接的说明了interrupt()并不是中断线程,而是发送一个中断的信号

如果在运行时代码中没有抛出InterruptException的方法,那么就需要频繁的调用Thread类的静态方法interrupt()来判断是否收到中断信号

public class Main {

	public static void main(String[] args) throws InterruptedException {
		Thread t = new Thread() {
			@Override
			public void run() {
				int i = 0;
				while(true) {
					if(!Thread.interrupted()) {
						i++;
						System.out.println(i);
					}else {
						System.out.println("被中断");
						return;
					}
				}
			}
		};
		t.start();
		Thread.sleep(10);
		t.interrupt();
	}
 
}

//判断当前线程是否被中断,如果这个方法被连续调用两次,那么第二次调用返回false
//(除非第一次调用已经清除了中断状态在第二次调用之前)
//如果线程失活,那么返回false
public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

中断状态标记

中断机制的实现是通过一个标记中断状态(interrupt status)实现的。我们通过调用某个线程对象的interrupt方法来设置这个标记。当一个线程通过Thread的类的静态方法interrupted判断到自己被中断后,立即会将这个状态清空。在其他的线程中,我们可以通过调用某个线程对象的isInterrupted方法判断这个线程是否被中断,但是不会中断状态清空。

今天学习结束 ...歇歇  

猜你喜欢

转载自blog.csdn.net/qq_35815781/article/details/85777382