任务管理器里面的exe 就是一个进程,而线程是进程的子任务,一个进程可以有多个线程,开发多线程是为了更好的利用CPU资源,现在的电脑基本上都是多核处理器,而CPU并没有真正发挥其作用,所以多线程技术上场,多线程开发就是让CPU不停的切换上下文,上下文切换速度还是比较快的,一毫秒内可以切换一次,达到看似并行的程序。
场景:对几个文档进行修改,程序需要做的是读取,修改。读取的时候需要程序等待,CPU是严重浪费了,由于是多个文件,第一个读取完了, 当第一个读取完的时候可以进行写操作,然后读取第二个文件,修改第二个文件,。。。。。流水线模式,但是现在是多核时代,可以用多线程让它“并行”执行。
下图右侧为多线程
使用多线程三种基本的用法
1 继承Thread
2 实现Runnable接口
3 实现Callable接口
实例:
1 继承Thread
public class TestThread extends Thread{ public TestThread(String name) { this.setName(name); } @Override public void run() { super.run(); System.out.println(Thread.currentThread().getName()); } /** * @param args */ public static void main(String[] args) { TestThread atest = new TestThread("A"); atest.start(); TestThread btest = new TestThread("B"); btest.start(); System.out.println("end"); } }
看看Thread为何物,打开JDK源码,发现这句话
public class Thread implements Runnable
它已经实现了Runnable接口,它是如何调用run()呢,找源码
源码
public void run() { if (target != null) { target.run(); } }
是被一个target调用了,看下target对象,target是Runnable接口,是哪里被调用了?在eclipse里面选中target右键点击references-->project
发现是在
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
方法里面初始化了它,再次查找init被谁调用
发现 Thread的有很多构造方法调用了它,本例子是下面的构造调用
public Thread(String name) { init(null, null, name, 0); }
上面这些知道了start是如何调用run方法。
a: 如果直接调用run会怎么样?
b: 如果同一个线程多次调用start会怎么样。
直接调用run方法,就不是异步执行了,结果显示线程名字为main,是一个叫main的线程调用了执行了main方法中的代码,它需要等待run方法执行完之后,才能执行后面的代码,所以这就是不是异步了。
如果多次调用start 发现报错了java.lang.IllegalThreadStateException,看jdk start的实现
public synchronized void start() { if (threadStatus != 0 || this != me) throw new IllegalThreadStateException(); group.add(this); start0(); if (stopBeforeStart) { stop0(throwableFromStop); } }
它标记了状态,threadStatus为1025,抛出异常。
发现end的打印,时而在前,时而在后, A,B线程名字的顺序也是毫无节奏,这说明多线程程序里面的代码调用与顺序毫无关系。
称为线程的随机性。为了验证其随机性,再写一个例子:
public class TestThread extends Thread{ public TestThread(String name) { this.setName(name); } @Override public void run() { super.run(); for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName()); } } /** * @param args */ public static void main(String[] args) { TestThread test = new TestThread("A"); test.start(); for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName()); } } }
Thread的的构造有好多个,JDK源码可以看到,可以传递Runnable,JDK里面很多类实现了该接口,Thread也实现了该接口,可以使用很多的类作为参数,传递Thread对象那么可以这么写。
TestThread atest = new TestThread("A");
Thread th = new Thread(atest,"TH");
关于this.getName(),它可以获取到该类的线程名,Thread.currentThread()可以获取到任何的当前运行的线程
this.getName()不推荐用,Thread.currentThread()可以被main方法调用,而this.getName()不行,因为main是静态方法,没法去调用this,this只能被非静态方法调用。
举例1:
public class TestThread extends Thread{ public static TestThread test = new TestThread("TH"); public TestThread(String name) { this.setName(name); } @Override public void run() { super.run(); for (int i = 0; i < 3; i++) { /*System.out.println("Thread.currentThread()-->"+Thread.currentThread().getName()); System.out.println("this-->"+this.getName());*/ System.out.println("Thread.currentThread()-->"+Thread.currentThread().isAlive()); System.out.println("this-->"+this.isAlive()); } System.out.println("------------>"+(this.hashCode())); } /** * @param args */ public static void main(String[] args) { TestThread atest = new TestThread("A"); System.out.println("main hashcode:"+ atest.hashCode()); atest.start(); for (int i = 0; i < 3; i++) { // System.out.println("main()-->"+Thread.currentThread().getName()); System.out.println("main()-->"+Thread.currentThread().isAlive()); } } }
举例2:
public class TestThread extends Thread{ public static TestThread test = new TestThread("TH"); public TestThread(String name) { this.setName(name); } @Override public void run() { super.run(); for (int i = 0; i < 3; i++) { /*System.out.println("Thread.currentThread()-->"+Thread.currentThread().getName()); System.out.println("this-->"+this.getName());*/ System.out.println("Thread.currentThread()-->"+Thread.currentThread().isAlive()); System.out.println("this-->"+this.isAlive()); } System.out.println("------------>"+(this.hashCode())); } /** * @param args */ public static void main(String[] args) { TestThread atest = new TestThread("A"); Thread th = new Thread(atest,"TH"); System.out.println("main hashcode:"+ th.hashCode()); th.start(); for (int i = 0; i < 3; i++) { // System.out.println("main()-->"+Thread.currentThread().getName()); System.out.println("main()-->"+Thread.currentThread().isAlive()); } } }
举例1和举例2的结果区别在this.isAlive() 结果不同,从结果可知直接继承Thread的类,直接调用start,hashcode相同, this.isAlive()调用才是活动状态,通过Thread(Runnable)构造的话 ,hashcode不同,this对象指向的对象已不是该类的对象了。利用构造方法th是一个线程,而参数也是一个线程,他们是2个线程
,atest 也可以成为th的子线程,就是target对象
2 实现Runnable,Callable接口
继承Thread明显有缺陷,Java的单继承决定了扩展性,但是多线程提供了Runnable接口
例子略