前言
介绍Java实现多线程的机制,以及Java的反射机制。同时还包括Java的注解。
目录
一、Java线程概述
启动JVM时就是创建了一个进程,而一个进程又可以创建多个线程。运行Java程序时JVM至少有两个线程,一个是主线程去执行main( )方法,另一个进行垃圾回收。
Java中,进程之间的内存不共享,线程之间栈区的内存不共享,堆区和方法区的内存共享。
二、Java创建线程的方式
1、Java创建线程的三种方式
(1)、编写类继承java.lang.Thread,并重写run( )方法,然后创建对象,调用对象的start( )方法
//创建线程的第一种方式:
//创建一个类继承Thread,重写run()方法,然后创建该对象,调用对象的start()方法启动线程
//注意Thread是java.lang包下的,所以不需要导包
class ThreadTest1 extends Thread{
@Override
public void run() {
System.out.println("新的线程创建了");
}
}
public class Test01 {
public static void main(String[] args) {
//创建线程对象,调用线程的start()方法
ThreadTest1 t = new ThreadTest1();
t.start();
System.out.println("这是主线程");
}
}
运行一次的输出结果是:
这是主线程
新的线程创建了因为是不同的线程,所以这两句话的输出的先后顺序不一定。不过由于线程的创建和调用方法需要时间,因此这个例子中main( )方法中的输出会先进行。可以在输出之前加个循环,循环几十万次,然后再输出。
调用start( )方法启动线程时,会创建一个新的栈空间。不过start( )方法仍然是在当前栈也就是主栈完成的,start( )方法完成,创建好栈空间后,会自动将run( )方法压栈,调用该方法。
(2)、实现java.lang.Runnable接口,也是重写run( )方法,然后创建对象,传入Thread的有参构造方法中,再调用创建的Thread对象的start( )方法
//创建对象的第二种方式:
//实现Runnable接口,重写run()方法。然后创建对象传入Thread的有参构造中,调用Thread对象的start()方法
class ThreadTest2 implements Runnable{
@Override
public void run() {
System.out.println("利用第二种方式创建线程");
}
}
public class Test02 {
public static void main(String[] args) {
ThreadTest2 rt = new ThreadTest2();
Thread t = new Thread(rt);
//也是调用start()方法创建新的栈然后调用run()方法
t.start();
System.out.println("主线程");
}
}
(3)、实现Callable接口,重写call()方法。创建对象传入FutureTask的有参构造中,然后将FutureTask再传入Thread的有参构造,调用start()方法
//创建线程的第三种方式:
//实现Callable接口,重写call()方法。创建对象传入FutureTask的有参构造中,然后将FutureTask再传入Thread的有参构造,调用start()方法
//该接口在java.util包下
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class ThreadTest3 implements Callable {
@Override
public Object call() throws Exception {
System.out.println("要重写call()方法");
return "返回值";
}
}
public class Test03 {
public static void main(String[] args) {
ThreadTest3 ct = new ThreadTest3();
//需要传入到未来任务类FutureTask的有参构造中
FutureTask ft = new FutureTask(ct);
//然后将FutureTask再传入Thread的有参构造,调用start()方法
Thread t = new Thread(ft);
t.start();
Object o = null;
//这种方式创建的线程可以拿到返回值
//注意,是用传入Thread中的未来任务类FutureTask的get()方法拿到的Callable中call()方法的返回值
try {
o = ft.get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
System.out.println(o);//返回值
}
}
注意,该类创建线程的方式要导入java.util包。
这种方式创建线程的特点在于可以得到线程的返回值,使用未来任务类FutureTask的get( )方法可以的到Callable中call( )方法的返回值。但是这个方法会造成当前线程阻塞。
2、对线程的一些操作
(1)、设置和获取线程的名字
使用setName( )和getName( )方法修改和得到线程的名字
(2)、获取当前线程对象
使用Thread.currentThread( )方法返回一个线程对象
(3)、让线程睡眠的方法
用Thread.sleep( )方法让当前线程睡眠 ,用实例方法interraput( )方法终止睡眠。注意,终止睡眠只能是终止别的线程,不能终止当前线程。
(4)、终止线程的方法
在线程类中添加一个布尔类型的标记属性
class ThreadTest4 extends Thread{
//为线程终止设置一个标记
private boolean run = true;
@Override
public void run() {
for(int i = 1;i<100;i++){
if(run){
//让当前线程睡眠的方法
try {
Thread.sleep(100);//传入的是毫秒数,该方法有异常
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("支线程第"+i+"次执行");
}
else{
return;
}
}
}
public boolean isRun() {
return run;
}
public void setRun(boolean run) {
this.run = run;
}
}
public class Test04{
public static void main(String[] args) {
ThreadTest4 t1 = new ThreadTest4();
t1.start();
//设置和得到线程名字
System.out.println(t1.getName());//Thread-0
t1.setName("线程2");
System.out.println(t1.getName());//线程2
//获取当前线程
Thread t2 = Thread.currentThread();
System.out.println(t2.getName());//main
//让当前线程睡眠的方法
try {
Thread.sleep(5000);//传入的是毫秒数,该方法有异常
} catch (InterruptedException e) {
e.printStackTrace();
}
//终止线程睡眠,注意不能终止当前线程的睡眠
t1.interrupt();
//用标记终止线程
t1.setRun(false);//只执行到45次就停止了
}
}
三、线程调度
1、Java线程调度的机制和常用方法
Java的线程调度采用的是抢占式的方式,各线程具有优先级可用线程对象的getPriority( )方法和setPriority( )方法来获取和设置线程执行的优先级。数字越大,代表优先级越高。最高优先级为10,最低为1,默认优先级为5。
静态方法Thread.yield( )方法会暂停当前对象,将当前对象状态由执行转换为就绪,而不是阻塞。并开始其它就绪线程。
实例方法join( )会将调用该方法的线程对象合并到当前线程,会使原来的线程阻塞。
public class Test05 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1;i<100;i++){
System.out.println("支线程第"+i+"次执行");
}
}
});
t.start();
//默认优先级是5
System.out.println(t.getPriority());//5
//将t线程合并到当前线程,该方法有异常
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i = 1;i<10;i++){