多线程的基本概念

      任务管理器里面的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接口

  例子略

 

 

 

 

 

 

 

 

 

 

 

猜你喜欢

转载自hangzhoujava.iteye.com/blog/2311991