一、java多线程的实现
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口;Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限。
public class Thread extends Object implements Runnable ,Thread类也是Runnable接口的子类。
1. 通过继承Thread类实现多线程
public class ThreadTest extends Thread { private String threadName; public ThreadTest(String threadName) { super(); this.threadName = threadName; } public void run() { for (int i = 0; i < 5; i++) { System.out.println("线程开始:" + this.threadName + ",i=" + i); } } }
public static void main(String[] args) { ThreadTest t1=new ThreadTest("线程a"); ThreadTest t2=new ThreadTest("线程b"); t1.start(); t2.start(); }
输出的结果可能是这样(因为需要用到CPU的资源来分配线程,所以每次的运行结果可能是不一样的):
线程开始:线程a,i=0
线程开始:线程b,i=0
线程开始:线程a,i=1
线程开始:线程b,i=1
线程开始:线程a,i=2
线程开始:线程b,i=2
线程开始:线程a,i=3
线程开始:线程a,i=4
线程开始:线程b,i=3
线程开始:线程b,i=4
注意:虽然我们在这里调用的是start()方法,但是实际上调用的还是run()方法的主体,通过查看源代码代码找到Thread中的start()方法的定义,可以发现此方法中使用了private native void start0();其中native关键字表示可以调用操作系统的底层函数,那么这样的技术成为JNI技术(java Native Interface)
2. 通过实现Runnable接口实现多线程
在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成。
public class ThreadTest implements Runnable { private String threadName; public ThreadTest(String threadName) { super(); this.threadName = threadName; } public void run() { for (int i = 0; i < 5; i++) { System.out.println("线程开始:" + this.threadName + ",i=" + i); } } }
public static void main(String[] args) { ThreadTest t1=new ThreadTest("线程a"); ThreadTest t2=new ThreadTest("线程b"); new Thread(t1).start(); new Thread(t2).start(); }
可能的输出结果为(因为需要用到CPU的资源来分配线程,所以每次的运行结果可能是不一样的):
线程开始:线程a,i=0
线程开始:线程a,i=1
线程开始:线程a,i=2
线程开始:线程b,i=0
线程开始:线程b,i=1
线程开始:线程b,i=2
线程开始:线程b,i=3
线程开始:线程b,i=4
线程开始:线程a,i=3
线程开始:线程a,i=4
在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。
3. 两种实现方式的区别和联系
在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处:
a.避免点继承的局限,一个类可以继承多个接口。 b.适合于资源的共享
public class MyThread extends Thread { private int ticket = 10; public void run() { for (int i = 0; i < 20; i++) { if (this.ticket > 0) { System.out.println(Thread.currentThread().getName() + "卖票:ticket" + this.ticket--); } } } }
public static void main(String[] args) { MyThread t1=new MyThread(); MyThread t2=new MyThread(); t1.start(); t2.start(); }
可能的输出:
Thread-0卖票:ticket10
Thread-1卖票:ticket10
Thread-0卖票:ticket9
Thread-1卖票:ticket9
Thread-0卖票:ticket8
Thread-1卖票:ticket8
Thread-0卖票:ticket7
Thread-1卖票:ticket7
Thread-0卖票:ticket6
Thread-1卖票:ticket6
Thread-0卖票:ticket5
Thread-1卖票:ticket5
Thread-0卖票:ticket4
Thread-1卖票:ticket4
Thread-0卖票:ticket3
Thread-1卖票:ticket3
Thread-0卖票:ticket2
Thread-1卖票:ticket2
Thread-0卖票:ticket1
Thread-1卖票:ticket1
说明:两个线程,每个线程都在卖自己的票,总共10张票,结果却卖了20张,这就没有达到资源的共享。
注意:在Thread中就不可以用同一个实例化对象t1,否则会报错。
t1.start(); t1.start();//会报错
如果用Runnable就可以实现资源共享,下面看例子:
package Others; public class MyThread implements Runnable { private int ticket = 10; public void run() { for (int i = 0; i < 20; i++) { if (this.ticket > 0) { System.out.println(Thread.currentThread().getName() + "卖票:ticket" + this.ticket--); } } } }
public static void main(String[] args) { MyThread t1=new MyThread(); new Thread(t1).start(); new Thread(t1).start(); }
输出:
Thread-0卖票:ticket10
Thread-1卖票:ticket9
Thread-0卖票:ticket8
Thread-1卖票:ticket7
Thread-0卖票:ticket6
Thread-1卖票:ticket5
Thread-0卖票:ticket4
Thread-1卖票:ticket3
Thread-0卖票:ticket2
Thread-1卖票:ticket1
说明:虽然现在程序中有2个线程,但是一共卖了10张票,也就是说使用Runnable实现多线程可以达到资源共享目的。
但是在正常环境中,卖票的网络可能会出现延迟,例如我们将代码修改成这样:
public class MyThread implements Runnable { private int ticket = 10; public void run() { for (int i = 0; i < 20; i++) { if (this.ticket > 0) { try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖票:ticket" + this.ticket--); } } } }
可能输出的结果就成了:
Thread-1卖票:ticket10
Thread-0卖票:ticket9
Thread-0卖票:ticket8
Thread-1卖票:ticket8
Thread-1卖票:ticket7
Thread-0卖票:ticket6
Thread-1卖票:ticket5
Thread-0卖票:ticket4
Thread-0卖票:ticket3
Thread-1卖票:ticket3
Thread-0卖票:ticket2
Thread-1卖票:ticket2
Thread-0卖票:ticket1
Thread-1卖票:ticket0
说明:因为一个线程就有可能在还没有对票数进行操作没有结束的时候,其他线程就已经获取当前票数了。
要解决这样的问题,就要使用同步,同步指的是在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才能继续进行。
修改代码如下:
public class MyThread implements Runnable { private int ticket = 10; public void run() { synchronized (this) { for (int i = 0; i < 20; i++) { if (this.ticket > 0) { try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖票:ticket" + this.ticket--); } } } } }
public static void main(String[] args) { MyThread t1=new MyThread(); new Thread(t1).start(); new Thread(t1).start(); }
输出:
Thread-0卖票:ticket10
Thread-0卖票:ticket9
Thread-0卖票:ticket8
Thread-0卖票:ticket7
Thread-0卖票:ticket6
Thread-0卖票:ticket5
Thread-0卖票:ticket4
Thread-0卖票:ticket3
Thread-0卖票:ticket2
Thread-0卖票:ticket1
还可以使用同步方法来提高效率:
public class MyThread implements Runnable { private int ticket = 10; public void run() { sale(); } public synchronized void sale(){ for (int i = 0; i < 20; i++) { if (this.ticket > 0) { try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖票:ticket" + this.ticket--); } } } }
main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM实习在就是在操作系统中启动了一个进程。
二、线程的优先级和线程让步
t.setPriority(8);
t.start();
线程可以具有的最高优先级。
static int MIN_PRIORITY
线程可以具有的最低优先级。
static int NORM_PRIORITY
分配给线程的默认优先级。
t.start();
t.join();
public class TestDeadLock implements Runnable { public int flag = 1; static Object o1= new Object(); static Object o2 = new Object(); /** * @param args */ public static void main(String[] args) { TestDeadLock td1 = new TestDeadLock(); TestDeadLock td2 = new TestDeadLock(); td1.flag=1; td2.flag=0; Thread t1 = new Thread(td1); Thread t2 = new Thread(td2); t1.start(); t2.start(); } public void run() { if(flag == 1){ synchronized(o1){ try { Thread.sleep(500); } catch (InterruptedException e) { } synchronized(o2){ System.out.println("1"); } } } if(flag==0){ synchronized(o2){ try { Thread.sleep(500); } catch (InterruptedException e){ } synchronized(o1){ System.out.println("0"); } } } } }
说明:t1线程等待t2线程,t2线程又等待t1线程,这样互相等待就造成了多线程的死锁。