例如:网上购物时邮件通知程序,用户单击提交按钮确认订单时,一方面要显示信息提示用户订单已经确认,一方面应该自动给用户发送一份电子邮件。
二、实例化线程
要重写继承的run()方法,run()方法中的代码就是线程所要执行任务的描述。
一旦线程启动,run方法中的代码将成为一条独立的执行线索。
注意,重写后的run方法虽然具有成为执行线索的能力,但也可以作为一般的方法调用,直接调用run方法并不产生新的执行线索
若想得到一个线程,必须创建Thread类或其子类对象,这时就要使用Thread类的特定构造器来完成这个工作。
//创建实现Runnable接口的类的对象。
MyRunnable mr=new MyRunnable();
Thread t=new Thread(mr);
这种方式实际上是告诉线程对象要执行的任务run方法在哪里。
实现Runnable接口的类的对象只是指出了线程需要完成的任务,其本身并不是线程对象。
* 实现Runnable接口的类
*
* @author leizhimin 2008-9-13 18:12:10
*/
public class DoSomething implements Runnable {
private String name;
public DoSomething(String name) {
this.name = name;
}
public void run() {
for (int i = 0; i < 5; i++) {
for (long k = 0; k < 100000000; k++) ;
System.out.println(name + ": " + i);
}
}
}
* 测试Runnable类实现的多线程程序
*
*/
public class TestRunnable {
public static void main(String[] args) {
DoSomething ds1 = new DoSomething("阿三");
DoSomething ds2 = new DoSomething("李四");
Thread t1 = new Thread(ds1);
Thread t2 = new Thread(ds2);
t1.start();
t2.start();
}
}
阿三: 0
李四: 1
阿三: 1
李四: 2
李四: 3
阿三: 2
李四: 4
阿三: 3
阿三: 4
Process finished with exit code 0
* 测试扩展Thread类实现的多线程程序
*
*/
public class TestThread extends Thread{
public TestThread(String name) {
super(name);
}
public void run() {
for(int i = 0;i<5;i++){
for(long k= 0; k <100000000;k++);
System.out.println(this.getName()+" :"+i);
}
}
public static void main(String[] args) {
Thread t1 = new TestThread("阿三");
Thread t2 = new TestThread("李四");
t1.start();
t2.start();
}
}
李四 :0
阿三 :1
李四 :1
阿三 :2
李四 :2
阿三 :3
阿三 :4
李四 :3
李四 :4
一旦处于准备状态的线程获取了CPU时间,就进入运行状态,在运行状态下,线程随时可能被调度程序调度到准备状态,线程在执行时,由于需要等待某些必要条件可能会进入等待阻塞状态。
同时又几个线程能处于运行状态取决于硬件,如果是双核(每核心一线程)CPU,同一时刻可能有两个线程处于运行状态。
java自动调度没有逻辑约束的线程时,其执行顺序是没有保障的,但是可以通过编程调用一些调度线程的方法,来实现一定程度上对线程的调度。
有些调度方法是有保障的,有些只是影响线程进入运行状态的概率。
静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到准备状态。
这两个方法不是与某个线程对象相关联的,其可以出现在任何位置,当执行到该方法时,让执行此方法的线程进入睡眠状态。
注意线程醒来将进入准备状态,并不能保证立即执行,因此指定的时间是线程暂停执行的最小时间。
Thread.sleep(123);
} catch (InterruptedException e) {
e.printStackTrace();
}
//定义实现Runnable接口的类
class MyRunnable1 implements Runnable
{
//重写run方法,指定该线程执行的代码
public void run()
{
for(int i=0;i<5;i++)
{
System.out.println("["+i+"] 我是线程1!!!");
//使此线程进入睡眠状态
try
{
Thread.sleep(100);
}
catch(InterruptedException ie)
{
ie.printStackTrace();
}
}
}
}
//定义另外一个实现Runnable接口的类
class MyRunnable2 implements Runnable
{
//重写run方法,指定该线程执行的代码
public void run()
{
for(int i=0;i<5;i++)
{
System.out.println("<"+i+"> 我是线程2!!!");
//使此线程进入睡眠状态
try
{
Thread.sleep(100);
}
catch(InterruptedException ie)
{
ie.printStackTrace();
}
}
}
}
public class Sample16_3
{
public static void main(String[] args)
{
//创建实现Runnable接口的类
MyRunnable1 mr1=new MyRunnable1();
MyRunnable2 mr2=new MyRunnable2();
//创建线程Thread对象,并指定各自的target
Thread t1=new Thread(mr1);
Thread t2=new Thread(mr2);
//启动线程t1
t1.start();
//使主线程进入睡眠状态
try
{
Thread.sleep(5);
}
catch(InterruptedException ie)
{
ie.printStackTrace();
}
//启动线程t2
t2.start();
}
}
在没有特别指定的情况下,主线程的优先级为5,对于子线程,其初始优先级与其父线程的优先级相同。
JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,当前运行的线程优先级将大于或等于线程池中任何线程的优先级。但这仅仅是大多数情况。
t.setPriority(8);
t.start();
java中的线程优先级是依赖于本地平台的,在实际运行时会将线程在java中的优先级映射到本地的某个优先级。这样,如果本地提供的优先级比10个要少,则java中的不同的优先级可能会映射成相同的本地优先级,而具有基本相同的执行概率。
class MyThread1 extends Thread
{
public void run()
{
for(int i=0;i<=49;i++)
{
System.out.print("<xiao"+i+"> ");
}
}
}
class MyThread2 extends Thread
{
public void run()
{
for(int i=0;i<=49;i++)
{
System.out.print("[大"+i+"] ");
}
}
}
public class aa
{
public static void main(String[] args)
{
MyThread1 t1=new MyThread1();
MyThread2 t2=new MyThread2();
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
}
}
这里并不是说高优先级的一直占用CPU,只是说高优先级的争夺的机会大,交替执行还是存在的。
二:线程将给指定的线程让步,指定线程没有完成,其绝不恢复执行。
yield()方法
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到准备状态,但有可能没有效果。
class MyRunnable implements Runnable
{
private String flagl;
private String flagr;
public MyRunnable(String flagl,String flagr)
{
this.flagl=flagl;
this.flagr=flagr;
}
public void run()
{
for(int i=0;i<30;i++)
{
System.out.print(flagl+i+flagr);
//调用yield方法使当前正在执行的线程让步
Thread.yield();
}
}
}
public class Sample16_5
{
public static void main(String[] args)
{
//创建两个实现Runnable接口的类的对象
MyRunnable mr1=new MyRunnable("[","] ");
MyRunnable mr2=new MyRunnable("<","> ");
//创建两个线程Thread对象,并指定执行的target
Thread t1=new Thread(mr1);
Thread t2=new Thread(mr2);
//启动线程t1、t2
t1.start();
t2.start();
}
}
这里线程时交替执行的,但是是没有保障的,有时交替有时无法做到。
join()方法
当一个线程必须等待另一个线程执行完毕时,才恢复执行时使用join方法。是有保障的。
class MyThread extends Thread
{
public void run()
{
for(int i=0;i<30;i++)
{
System.out.print("["+i+"] ");
}
System.out.print("{子线程执行结束} ");
}
}
public class Sample16_6
{
public static void main(String[] args)
{
Thread t=new MyThread();
t.start();
for(int i=0;i<30;i++)
{
if(i==10)
{
//主线程中调用join方法使主线程进行让步
try
{
System.out.print("{使用了Jion方法} ");
t.join();//等待该线程终止。
}
catch(InterruptedException ie)
{
ie.printStackTrace();
}
}
System.out.print("<"+i+"> ");
}
}
}