Java线程(1)---多线程

面试时热门问题之一.......


线程

同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
线程是cpu调度的最小单位。

线程状态:
线程状态和进程相同,也有5个状态:创建、就绪、运行、阻塞、终止,生命周期如下图:

图源:http://www.runoob.com/java/java-multithreading.html

多线程

多线程指的是在同一个进程中有多个线程同时在执行。java实现多线程的常用方法有两种:继承Thread类和实现Runable接口,下面都会有相应的介绍。

继承Thread类

简单实现如下:

public class thread1 extends Thread{
	private String name;
    public thread1(String name) {
       this.name=name;
    }
	public void run() {
        for (int i = 0; i < 8; i++) {
            System.out.println(name + "输出:  " + i);
            try {
                Thread.sleep((int) Math.random() * 100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
       
	}
}

 

测试运行:

public class test {
	 
		public static void main(String[] args) {
			thread1 t1=new thread1("A");
			thread1 t2=new thread1("B");
			t1.start();
			t2.start();
	 
		}
	 
	}

结果为:

再次运行结果为:

可以发现,两次程序运行的输出顺序有所不同,也就是多线程执行代码的顺序是不确定的。当我们执行test时,主线程在main()时调用,之后创建两个线程并调用其start方法来启动两个线程,这样便实现了多线程。

要注意的是:在调用start()方法时,只是把这个线程的状态变成Runnable状态,具体什么时候执行要由操作系统决定,这涉及到线程调度的问题,后续再说。每个线程的start()方法只能调动一次,多次调用将报错。代码中的sleep()方法是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。

 

其实,main方法其实也是一个线程。在java中所有的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。java中每次运行至少都有两个线程,一个是main线程,一个是垃圾回收线程(垃圾回收在后续的博客中会提到)。

实现Runable接口

简单代码实现:

public class thread2  implements Runnable{

	private String name;
	public thread2(String name)
	{
		this.name=name;
	}
	public void run()
	{
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "输出 :  " + i);
            try {
                Thread.sleep((int) Math.random() * 100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
       
	}

}

测试代码:

public class test {
	 
		public static void main(String[] args) {
				thread2 t1=new thread2("C");
				thread2 t2=new thread2("D");
				Thread T1=new Thread(t1);
				Thread T2=new Thread(t2);
				T1.start();
				T2.start();
		}
	 
	}

输出结果:

再次运行后结果:

可以发现,运行结果与之前继承Thread类的结果类似,run()方法是多线程的一个特征。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
但是要注意的是,实现Runnable接口来实现多线程时,不能直接调用start()方法(如上述代码中,无法使用t1.start()。),所有的线程启动都需要通过Thread的start()方法来运行。因此了解Thread类的API很关键,查看源码可发现Thread有多种方法如下:

public void start()
//使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
public void run()
//如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法//不执行任何操作并返回。
public final void setName(String name)
//改变线程名称,使之与参数 name 相同。
public final void setPriority(int priority)
//更改线程的优先级。
public final void setDaemon(boolean on)
//将该线程标记为守护线程或用户线程。
public final void join(long millisec)
//等待该线程终止的时间最长为 millis 毫秒。
public void interrupt()
//中断线程。
public final boolean isAlive()
//测试线程是否处于活动状态。
public static void yield()
//暂停当前正在执行的线程对象,并执行其他线程。
public static void sleep(long millisec)
//在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确//性的影响。
public static boolean holdsLock(Object x)
//当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
public static Thread currentThread()
//返回对当前正在执行的线程对象的引用。
public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流。


Thread和Runnable的区别
基本用法上的区别不是很大,但是我们知道Java中一个类只能继承一个父类,而接口可以实现多个,因此在实际运用中常用Runnable,使用Runnable的优势有:
1.避免了java单继承的限制
2.增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
3.线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类(线程池后面的博客会提到)

 

还有一些其他的实现多线程的方法如通过 Callable Future 创建线程,这里做一个简单的介绍:
简答实现如下:

public class thread3 implements Callable<Integer>{

	/**
	 * @param args
	 */
	public Integer call()
	{
		int sum=0;;
		for(int i=0;i<10;i++)
		{
			sum=sum+i;
			//System.out.println(Thread.currentThread().getName()+" "+sum);
		}
		return sum;
	}
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		// TODO Auto-generated method stub
       thread3 t3=new thread3();
       FutureTask<Integer> ft=new FutureTask<Integer>(t3);
       Thread T3=new Thread(ft);
       T3.start();
       System.out.println(ft.get()) ;
	}

}

使用方法:
实现Callable 接口,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值(这是与前二者不同的地方)
使用时需要使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象用于存储返回值。
使用 FutureTask 对象作为 Thread 对象的 target 来创建并启动新线程。
调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

 

有效利用多线程用于处理程序并发执行的,通过对多线程的使用,可以编写出非常高效的程序,但是如果线程过多,由于上下文切换的开销就会很大,此时多线程的效率就不高了。

 

附:线程调度算法
先来先服务(First Come First Served,FCFS )
最短作业(进程)优先(SJF:Shortest Job First SPF:Shortest Process First)
响应比最高者优先算法(HRRF:Highest Response Ratio First)
优先级调度算法
时间片轮转调度算法(Round Robin,RR)
多级反馈队列调度算法(MLFQ:Multi-level Feedback Queue)


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值