【Java系列-2】多线程的实现及源码深入

一、多线程的实现方式

多线程的3种启动方式相信都有听说过:

1.继承Thread类: 重写run()方法,直接threadClass.start()方法启动。

2.实现Runnable接口:重写run()方法,new Thead(runnableClass).start()方法启动。

3.实现Callable接口:重写call方法,new Thread(new FutureTask<T>(callableClass)).start()方法启动。

 

简单总结如下:

1.3种实现如上。

2.推荐使用Runnable接口的方法实现,可以避免继承Thread类的单继承局限,也可以避免callable接口的复杂。

3.虽然有多种方法,但是启动多线程的方法只有一个,通过thread类的start()方法,因为start()方法本质是实现原生操作系统的start0()方法。

4.Runnable接口实现比Thread类实现可以更好的体现数据共享的概念。

 

下面是详细的总结:

1.1  多线程实现

范例1-1:定义一个线程操作:继承Thread类

since JDK1.0

java.lang.Thread类的定义如下:public class Thread implements Runnable{}

 

class MyThread extends Thread {                         // 这就是一个多线程的操作类

         private String name ;                                       // 定义类中的属性

         public MyThread(String name) {            // 定义构造方法

                   this.name = name ;

         }

         @Override

         public void run() {                                                    // 覆写run()方法,作为线程的主操作方法

                   for (int x = 0 ; x < 200 ; x ++) {

                            System.out.println(this.name + " --> " + x);

                   }

         }

}

范例1-2:启动多线程:thread.start()

public class TestDemo {                                                                     // 主类

         public static void main(String[] args) {

                   MyThread mt1 = new MyThread("线程A") ;     // 实例化多线程类对象

                   MyThread mt2 = new MyThread("线程B") ;     // 实例化多线程类对象

                   MyThread mt3 = new MyThread("线程C") ;    // 实例化多线程类对象

                   mt1.start();                                                                                     // 启动多线程

                   mt2.start();                                                                                     // 启动多线程

                   mt3.start();                                                                                     // 启动多线程

         }

}

范例1-3:start()方法的源代码。

观察源码可以发现:

1.start方法会抛出异常:Runtime异常--IllegalThreadStateException();一个线程已经调用start方法后,再次调用就会抛出这个异常。

2.start0()方法:start()方法实际调用的是start0()方法,而start0()方法是原生的操作系统方法。由于线程的启动需要牵扯到系统的资源分配问题,所以具体的线程的实现在不同的操作系统上实际是不同的,但是JVM可以通过start0()方法可以实现在不同操作系统上进行资源的分配。

结论:只有Thread类的start()方法才能够进行操作系统的资源分配,所以启动多线程的方式永远都是调用thread类的start()方法。

public synchronized void start() {//start方法是同步的

    if (threadStatus != 0)

      throw new IllegalThreadStateException();

    group.add(this);

    boolean started = false;

    try {

        start0();

        started = true;

    } finally {

      try {

          if (!started) {

            group.threadStartFailed(this);

          }

      } catch (Throwable ignore) {

      }

   }

}

private native void start0();//操作系统原生的方法

关于native关键字的解释查看如下文章:

https://www.cnblogs.com/b3051/p/7484501.html

范例1-4:使用Runnable实现多线程:实现Runnable接口

Runnable接口的源码:@FunctionalInterface标记在接口上,“函数式接口”是指仅仅只包含一个抽象方法的接口。

@FunctionalInterface
public interface Runnable {
  public abstract void run();
}

class MyThread implements Runnable {           // 定义线程主体类

         private String name;                                                 // 定义类中的属性

         public MyThread(String name) {                     // 定义构造方法

                   this.name = name;

         }

         @Override

         public void run() {                                                             // 覆写run()方法

                   for (int x = 0; x < 200; x++) {

                            System.out.println(this.name + " --> " + x);

                   }

         }

}

范例1-5:利用Thread类启动多线程:由1.3可以知道,启动多线程只能通过Thread类的start()方法。

Thead类的构造方法,参数是Runnable类型:public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0);}

因此可以通过匿名函数启动多线程(继承Runnable接口):

new Thread(new Runnable() {
  public void run() {
    System.out.println("Good");}
}).start(); // 利用Thread启动多线程

public class TestDemo {

         public static void main(String[] args) {

                   MyThread mt1 = new MyThread("线程A") ;     // 实例化多线程类对象

                   MyThread mt2 = new MyThread("线程B") ;     // 实例化多线程类对象

                   MyThread mt3 = new MyThread("线程C") ;    // 实例化多线程类对象

                   new Thread(mt1).start();                                                       // 利用Thread启动多线程

                   new Thread(mt2).start();                                                    // 利用Thread启动多线程

                   new Thread(mt3).start();                                                    // 利用Thread启动多线程

         }

}

范例1-6:使用Lamda表达式实现多线程:函数式接口的特性

public class TestDemo {

         public static void main(String[] args) {

                   String name = "线程对象" ;

                   new Thread(() -> {

                            for (int x = 0; x < 200; x++) {

                                     System.out.println(name + " --> " + x);

                            }

                   }).start();;

         }

}

面试题:多线程的实现2种(先不说Callable接口)的实现和区别

1.继承Thread类:public class Thread implements Runnable{public synchronized void start() }

2.实现Runnable接口:public interface Runnable {public abstract void run();}

3.Runnable实现的多线程比Thread类实现的多线程更好的体现数据共享的概念(卖票程序体现)

4.推荐一定使用Runnable接口实现,可以避免多继承。

 

范例1-7:通过继承Thread类实现卖票程序。

package com.yootk.demo;

class MyThread extends Thread {                                 // 线程的主体类

         private int ticket = 5;                                                         // 一共5张票

         @Override

         public void run() {                                                             // 线程的主方法

                   for (int x = 0; x < 50; x++) {                    // 循环50次

                            if (this.ticket > 0) {

                                     System.out.println("卖票,ticket = " + this.ticket --);

                            }

                  }

         }

}

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   MyThread mt1 = new MyThread() ;                 // 创建线程对象

                   MyThread mt2 = new MyThread() ;                 // 创建线程对象

                   MyThread mt3 = new MyThread() ;                 // 创建线程对象

                   mt1.start() ;                                                                          // 启动线程

                   mt2.start() ;                                                                 // 启动线程

                   mt3.start() ;                                                                 // 启动线程

         }

}

//3个线程各自都有ticket=5的引用

卖票,ticket = 1
卖票,ticket = 5
卖票,ticket = 4
卖票,ticket = 3
卖票,ticket = 2
卖票,ticket = 1
卖票,ticket = 4
卖票,ticket = 3
卖票,ticket = 2
卖票,ticket = 1

范例1-8:利用Runnable接口来实现多线程。

package com.yootk.demo;

class MyThread implements Runnable {              // 线程的主体类

         private int ticket = 5;                                                         // 一共5张票

         @Override

         public void run() {                                                             // 线程的主方法

                  for (int x = 0; x < 50; x++) {                    // 循环50次

                            if (this.ticket > 0) {

                                     System.out.println("卖票,ticket = " + this.ticket --);

                            }

                   }

         }

}

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   MyThread mt = new MyThread();           // 创建线程对象

                   new Thread(mt).start() ;                                   // 启动线程

                   new Thread(mt).start() ;                                   // 启动线程

                   new Thread(mt).start() ;                                   // 启动线程

         }

}

输出:

卖票,ticket = 5
卖票,ticket = 4
卖票,ticket = 2
卖票,ticket = 1
卖票,ticket = 3

范例1-9:使用Thread类实现数据共享的概念。

package com.yootk.demo;

class MyThread extends Thread {                        // 线程的主体类

         private int ticket = 5;                                                // 一共5张票

         @Override

         public void run() {                                                    // 线程的主方法

                   for (int x = 0; x < 50; x++) {           // 循环50次

                            if (this.ticket > 0) {

                                     System.out.println("卖票,ticket = " + this.ticket --);

                            }

                   }

         }

}

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   MyThread mt = new MyThread(); // 创建线程对象

                   new Thread(mt).start() ;                         // 启动线程

                   new Thread(mt).start() ;                         // 启动线程

                   new Thread(mt).start() ;                         // 启动线程

         }

}

//3个线程对象占据一个mt对象(指向一个ticket)

范例1-10:启动多线程:实现callable接口,先定义一个线程主体类。

import java.util.concurrent.Callable;

class MyThread implements Callable<String> {             // 多线程主体类

         private int ticket = 10;                                                                          // 卖票

         @Override

         public String call() throws Exception {

                   for (int x = 0; x < 100; x++) {

                            if (this.ticket > 0) {                                                              // 还有票可以出售

                                     System.out.println("卖票,ticket = " + this.ticket--);

                            }

                   }

                   return "票已卖光!";                                                 // 返回结果

         }

}

范例1-11:启动多线程:实现callable接口

Since JDK1.5: java.util.concurrent.Callable接口---函数式接口,只有一个call方法

@FunctionalInterface
public interface Callable<V> {

  V call() throws Exception;
}

但是Thread类中并没有定义接受callable的构造函数,实现runnable接口的多线程的启动方式是通过FutureTask如下:new Thread(new FutureTask<String>(mt1)).start();

public interface Runnable {public abstract void run();}

public interface Future<V> {V get() throws InterruptedException, ExecutionException;}

public interface RunnableFuture<V> extends Runnable, Future<V> {void run();}

public interface Callable<V> {V call() throws Exception;}

public class FutureTask<V> implements RunnableFuture<V>{}

public FutureTask(Callable<V> callable)

public FutureTask(Runnable runnable, V result) {}

 

 

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   MyThread mt1 = new MyThread();                  // 实例化多线程对象

                   MyThread mt2 = new MyThread();                  // 实例化多线程对象

                   FutureTask<String> task1 = new FutureTask<String>(mt1) ;

                   FutureTask<String> task2 = new FutureTask<String>(mt2) ;

                   // FutureTask是Runnable接口子类,所以可以使用Thread类的构造来接收task对象

                   new Thread(task1).start();                     // 启动第一个线程

                   new Thread(task2).start();                     // 启动第二个线程

                   // 多线程执行完毕后可以取得内容,依靠FutureTask的父接口Future中的get()方法实现

                   System.out.println("A线程的返回结果:" + task1.get());

                   System.out.println("B线程的返回结果:" + task2.get());

         }

}

1.2  多线程常用操作方法

范例1-12:观察线程的命名:public Thread(Runnable target, String Threadname) {}

package com.yootk.demo;

class MyThread implements Runnable {

         @Override

         public void run() {

                   System.out.println(Thread.currentThread().getName());

         }

}

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   MyThread mt = new MyThread();

                   new Thread(mt, "自己的线程A").start();

                   new Thread(mt).start();

                   new Thread(mt, "自己的线程B").start();

                   new Thread(mt).start();

                   new Thread(mt).start();

         }

}

范例1-13:取得线程名字:Thread.currentThread().getName()

package com.yootk.demo;

class MyThread implements Runnable {

         @Override

         public void run() {

                   System.out.println(Thread.currentThread().getName());

         }

}

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   MyThread mt = new MyThread();

                   new Thread(mt, "自己的线程对象").start();

                   mt.run();                                // 直接调用run()方法,main

         }

}

范例1-14:观察休眠特点。

package com.yootk.demo;

class MyThread implements Runnable {

         @Override

         public void run() {

                   for (int x = 0; x < 10000; x++) {

                            try {

                                     Thread.sleep(1000);                                // 每次执行休眠1s

                            } catch (InterruptedException e) {

                                     e.printStackTrace();

                            }

                            System.out.println(Thread.currentThread().getName() + ",x = " + x);

                   }

         }

}

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   MyThread mt = new MyThread();

                   new Thread(mt, "自己的线程对象A").start();

         }

}

范例1-15:设置线程优先级:public final void setPriority(int newPriority) MAX_PRIORITY=10 |MIN_PRIORITY=1|NORM_PRIORITY =5

package com.yootk.demo;

class MyThread implements Runnable {

         @Override

         public void run() {

                   for (int x = 0; x < 20; x++) {

                            try {

                                     Thread.sleep(100);

                            } catch (InterruptedException e) {

                                     e.printStackTrace();

                            }

                            System.out.println(Thread.currentThread().getName() + ",x = " + x);

                   }

         }

}

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   MyThread mt = new MyThread();

                   Thread t1 = new Thread(mt, "自己的线程对象A");

                   Thread t2 = new Thread(mt, "自己的线程对象B");

                   Thread t3 = new Thread(mt, "自己的线程对象C");

                   T3.setPriority(Thread.MAX_PRIORITY);                        // 修改一个线程对象的优先级

                   t1.start();

                   t2.start();

                   t3.start();

         }

}

范例1-16:主方法优先级:5

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   System.out.println(Thread.currentThread().getPriority());

         }

}

//5

范例1-17:观察非同步情况下的操作。

package com.yootk.demo;

class MyThread implements Runnable {

         private int ticket = 5;                                                         // 一共有5张票

         @Override

         public void run() {

                   for (int x = 0; x < 20; x++) {

                            if (this.ticket > 0) {                                            // 判断当前是否还有剩余票

                                     try {

                                               Thread.sleep(100);                         // 休眠1s,模拟延迟

                                     } catch (InterruptedException e) {

                                               e.printStackTrace();

                                     }

                                     System.out.println(Thread.currentThread().getName()

                                                        + " 卖票,ticket = " + this.ticket--);

                            }

                   }

         }

}

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   MyThread mt = new MyThread();

                   new Thread(mt, "票贩子A").start();                  // 启动多线程

                   new Thread(mt, "票贩子B").start();                           // 启动多线程

                   new Thread(mt, "票贩子C").start();                           // 启动多线程

                   new Thread(mt, "票贩子D").start();                           // 启动多线程

         }

}

范例1-18:观察同步块:synchronized

package com.yootk.demo;

class MyThread implements Runnable {

         private int ticket = 5;                                                         // 一共有5张票

         @Override

         public void run() {

                   for (int x = 0; x < 20; x++) {

                            synchronized(this) {                                              // 定义同步代码块

                                     if (this.ticket > 0) {                                            // 判断当前是否还有剩余票

                                               try {

                                                        Thread.sleep(100);                         // 休眠1s,模拟延迟

                                               } catch (InterruptedException e) {

                                                        e.printStackTrace();

                                               }

                                               System.out.println(Thread.currentThread().getName()

                                                                 + " 卖票,ticket = " + this.ticket--);

                                     }

                            }

                   }

         }

}

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   MyThread mt = new MyThread();

                   new Thread(mt, "票贩子A").start();                  // 启动多线程

                   new Thread(mt, "票贩子B").start();                           // 启动多线程

                   new Thread(mt, "票贩子C").start();                           // 启动多线程

                   new Thread(mt, "票贩子D").start();                           // 启动多线程

         }

}

范例1-19:使用同步方法解决问题。

package com.yootk.demo;

class MyThread implements Runnable {

         private int ticket = 5;                                                         // 一共有5张票

         @Override

         public void run() {

                   for (int x = 0; x < 20; x++) {

                            this.sale();                                                        // 卖票操作

                   }

         }

         public synchronized void sale() {                        // 同步方法

                   if (this.ticket > 0) {                                                     // 判断当前是否还有剩余票

                            try {

                                     Thread.sleep(100);                                  // 休眠1s,模拟延迟

                            } catch (InterruptedException e) {

                                     e.printStackTrace();

                            }

                            System.out.println(Thread.currentThread().getName()

                                               + " 卖票,ticket = " + this.ticket--);

                   }

         }

}

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   MyThread mt = new MyThread();

                   new Thread(mt, "票贩子A").start();        // 启动多线程

                   new Thread(mt, "票贩子B").start();                  // 启动多线程

                   new Thread(mt, "票贩子C").start();                 // 启动多线程

                   new Thread(mt, "票贩子D").start();                 // 启动多线程

         }

}

范例1-20:程序死锁操作。

package com.yootk.demo;

class A {

         public synchronized void say(B b) {

                   System.out.println("A先生说:把你的本给我,我给你笔,否则不给!");

                   b.get();

         }

         public synchronized void get() {

                   System.out.println("A先生:得到了本,付出了笔,还是什么都干不了!");

         }

}

class B {

         public synchronized void say(A a) {

                   System.out.println("B先生说:把你的笔给我,我给你本,否则不给!");

                   a.get();

         }

         public synchronized void get() {

                   System.out.println("B先生:得到了笔,付出了本,还是什么都干不了!");

         }

}

public class TestDemo implements Runnable {

         private static A a = new A();                                  // 定义类对象

         private static B b = new B();                                  // 定义类对象

         public static void main(String[] args) throws Exception {

                   new TestDemo();                                                       // 实例化本类对象

         }

         public TestDemo() {                                                  // 构造方法

                   new Thread(this).start();                                 // 启动线程

                   b.say(a);                                                                     // 互相引用

         }

         @Override

         public void run() {

                   a.say(b);                                                                     // 互相引用

         }

}

1.4  线程间的经典操作案例——生产者与消费者案例

范例1-21:程序基本模型。

package com.yootk.demo;

class Message {

         private String title ;                                                             // 保存信息的标题

         private String content ;                                                       // 保存信息的内容

         public void setTitle(String title) {

                   this.title = title;

         }

         public void setContent(String content) {

                   this.content = content;

         }

         public String getTitle() {

                   return title;

         }

         public String getContent() {

                   return content;

         }

}

class Producer implements Runnable {                                   // 定义生产者

         private Message msg = null ;

         public Producer(Message msg) {

                   this.msg = msg ;

         }

         @Override

         public void run() {

                   for (int x = 0; x < 50; x++) {                              // 生产50次数据

                            if (x % 2 == 0) {

                                     this.msg.setTitle("李兴华") ;          // 设置title属性

                                     try {

                                               Thread.sleep(100) ;                        // 延迟操作

                                     } catch (InterruptedException e) {

                                               e.printStackTrace();

                                     }

                                     this.msg.setContent("Java讲师") ;         // 设置content属性

                            } else {

                                     this.msg.setTitle("mldn") ;                       // 设置title属性

                                     try {

                                               Thread.sleep(100) ;

                                     } catch (InterruptedException e) {

                                               e.printStackTrace();

                                     }

                                     this.msg.setContent("www.mldnjava.cn") ;     // 设置content属性

                            }

                   }

         }

}

class Consumer  implements Runnable {                               // 定义消费者

         private Message msg = null ;

         public Consumer (Message msg) {

                   this.msg = msg ;

         }

         @Override

         public void run() {

                   for (int x = 0; x < 50; x++) {                                       // 取走50次数据

                            try {

                                     Thread.sleep(100) ;                                           // 延迟

                            } catch (InterruptedException e) {

                                     e.printStackTrace();

                            }

                            System.out.println(this.msg.getTitle() + " --> " + this.msg.getContent());

                   }

         }

}

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   Message msg = new Message() ;                             // 定义Message对象,用于保存和取出数据

                   new Thread(new Producer(msg)).start() ;               // 启动生产者线程

                   new Thread(new Consumer(msg)).start() ;             // 取得消费者线程

         }

}

范例1-22:加入同步,解决数据错乱问题。

package com.yootk.demo;

class Message {

         private String title ;                                                             // 保存信息的标题

         private String content ;                                                       // 保存信息的内容

         public synchronized void set(String title, String content) {

                   this.title = title;

                   try {

                            Thread.sleep(200);

                   } catch (InterruptedException e) {

                            e.printStackTrace();

                   }

                   this.content = content;

         }

         public synchronized void get() {

                   try {

                            Thread.sleep(100);

                   } catch (InterruptedException e) {

                            e.printStackTrace();

                   }

                   System.out.println(this.title + " --> " + this.content);

         }

         // setter、getter略

}

class Producer implements Runnable {                                                      // 定义生产者

         private Message msg = null ;

         public Producer(Message msg) {

                   this.msg = msg ;

         }

         @Override

         public void run() {

                   for (int x = 0; x < 50; x++) {                                                // 生产50次数据

                            if (x % 2 == 0) {

                                     this.msg.set("李兴华","Java讲师") ;                          // 设置属性

                            } else {

                                     this.msg.set("mldn","www.mldnjava.cn") ;       // 设置属性

                            }

                   }

         }

}

class Consumer  implements Runnable {                                        // 定义消费者

         private Message msg = null ;

         public Consumer (Message msg) {

                   this.msg = msg ;

         }

         @Override

         public void run() {

                   for (int x = 0; x < 50; x++) {                                                // 取走50次数据

                            this.msg.get() ;                                                                             // 取得属性

                   }

         }

}

public class TestDemo {

         public static void main(String[] args) throws Exception {

                   Message msg = new Message() ;                                      // 定义Message对象,用于保存和取出数据

                   new Thread(new Producer(msg)).start() ;                        // 启动生产者线程

                   new Thread(new Consumer(msg)).start() ;                      // 取得消费者线程

         }

}

范例1-23:解决程序问题。

class Message {

         private String title;

         private String content;

         private boolean flag = true;

         // flag == true:表示可以生产,但是不能取走

         // flag == false:表示可以取走,但是不能生产

         public synchronized void set(String title, String content) {

                   if (this.flag == false) {                                              // 已经生产过了,不能生产

                            try {

                                     super.wait();                                           // 等待

                            } catch (InterruptedException e) {

                                     e.printStackTrace();

                            }

                   }

                   this.title = title;

                   try {

                            Thread.sleep(200);

                   } catch (InterruptedException e) {

                            e.printStackTrace();

                   }

                   this.content = content;

                   this.flag = false;                                                        // 已经生产完成,修改标志位

                   super.notify();                                                            // 唤醒等待线程

         }

         public synchronized void get() {

                   if (this.flag == true) {                                                // 未生产,不能取走

                            try {

                                     super.wait();                                           // 等待

                            } catch (InterruptedException e) {

                                     e.printStackTrace();

                            }

                   }

                   try {

                            Thread.sleep(100);

                   } catch (InterruptedException e) {

                            e.printStackTrace();

                   }

                   System.out.println(this.title + " --> " + this.content);

                   this.flag = true;                                                         // 已经取走了,可以继续生产

                   super.notify();                                                            // 唤醒等待线程

         }

         // setter、getter略

}

1.5  线程的生命周期

范例1-24:停止线程运行。

package com.yootk.demo;

class MyThread implements Runnable {

         private boolean flag = true;                                            // 定义标志位属性

         public void run() {                                                                       // 覆写run()方法

                   int i = 0;

                   while (this.flag) {                                                                // 循环输出

                            while (true) {

                                     System.out.println(Thread.currentThread().getName() + "运行,i = "

                                                        + (i++));                                           // 输出当前线程名称

                            }

                   }

         }

         public void stop() {                                                                     // 编写停止方法

                   this.flag = false;                                                                 // 修改标志位

         }

}

public class StopDemo {

         public static void main(String[] args) {

                   MyThread my = new MyThread();                   // 实例化Runnable接口对象

                   Thread t = new Thread(my, "线程");                // 建立线程对象

                   t.start() ;                                                                               // 启动线程

                   my.stop() ;                                                                            // 线程停止,修改标志位

         }

}

 

posted @ 2018-06-12 13:52  爱寂寞撒的谎言  阅读(194)  评论(0)    收藏  举报