实现 Runnable接口方式实现多线程
自定义类实现Runnable接口,将自定义类的对象传入Thread构造函数的参数。
Thread构造函数
Thread(Runnable target, String name)
分配一个新的 Thread对象
好处是不影响自定义类的继承,自定义类可以作为同一个资源供多个线程使用。
public class Mythread implements Runnable {
@Override
public void run() {
for (int i = 0; i <50; i++) {
System.out.println(i);
}
}
}
public class MytheadDemo {
public static void main(String[] args) {
Mythread mythread1=new Mythread();
Thread thread1=new Thread(mythread1,"线程1");
thread1.run();
}
}
实现Runnable接口底层是重写了Runnable的run方法,当new Thread会判断Runnable方法的run方法是否为空不为空则调用Runnable的run方法,当为空则调用自己的run方法。但是当如果我们自己写了Thread的run方法,那就是重写了Thread的run方法,那么就不会执行下面这个判断Runnable是否为空了!具体例子下面匿名类写法中有详细写。
继承Thead方法实现多线程
public class Thread
extends Object
implements Runnable
- 线程是程序中执行的线程。 Java虚拟机允许应用程序同时执行多个执行线程。
创建一个新的执行线程有两种方法:
-
一:是将一个类声明为Thread的子类。 这个子类应该重写Thread类的方法run。 然后可以分配并启动子类的实例。
1:创建Mythead继承Thread类
2:重写方法run
3:创建Mythread对象
4:启动线程 -
需要用到的两个Teread类的方法
1:public void run() 用来封装被线程重写的方法。 Thread的Thread应该覆盖此方法。
2:public void start()导致此线程开始执行; Java虚拟机调用此线程的run方法。结果是两个线程同时运行:当前线程(从调用返回到start方法)和另一个线程(执行其run方法)。 不止一次启动线程是不合法的。 特别地,一旦线程完成执行就可能不会重新启动。
异常 :IllegalThreadStateException - 如果线程已经启动。
public class Mythread extends Thread {
@Override
public void run() {
for (int i = 0; i <50; i++) {
System.out.println(i);
}
}
}
public class MytheadDemo {
public static void main(String[] args) {
Mythread thread1=new Mythread();
Mythread thread2=new Mythread();
thread1.start();
thread2.start();
}
}
匿名内部类
/**
*匿名内部类的方式启动线程
*/
public class T2 {
public static void main(String[] args) {
new Thread(){
public void run(){
System.out.println("thread1 start ... ");
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread2 start .... ");
}
}).start();
}
}
第1段相当于继承Thread的方式;第二段相当于实现Runnable的方式。
- 用lambda写法,下面两段代码相同效果,第二种更简洁。注意:用lambda方法进行简化,类或者接口只能有一个方法(Runnable只有一个方法)一个方法的接口用@FunctionalInterface修饰。
public static void main(String[] args) {
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类写法");
}
};
Thread thread=new Thread(runnable);
thread.start();
}
public static void main(String[] args) {
Runnable runnable= () -> { System.out.println("匿名内部类写法,用lambda写法");};
Thread thread=new Thread(runnable);
thread.start();
}
public static void main(String[] args) {
Thread thread=new Thread(() -> { System.out.println("匿名内部类写法");});
thread.start();
}
-
第二种
public Thread(Runnable target)分配一个新的Thread对象。 该构造函数具有与Thread (null, target, gname)相同的效果,其中gname是新生成的名称。 自动生成的名称格式为"Thread-"+ n ,其中n为整数。
参数
target - 启动此线程时调用其run方法的对象。 如果null ,这个类run方法什么都不做。 -
将上述两个方法合并:
package com.yy.concurrent.base1;
/**
*匿名内部类的方式启动线程
*/
public class T2 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("runnable");
}
}){
public void run(){
System.out.println("sub");
}
}.start();
}
}
最终输出结果是是:sub。为什么会这样呢?这就是重写导致的。
此时target不为空,应该是执行target.run(),即输出结果是Runnable,但是注意注意!!!!我们我看我们的代码,我们再T1类里面重写了run方法,所以执行的是重写的方法!
将上面的用普通方法写结果一样,Thread如果不重写run方法那么就运行的是Runnable的,如果重写那么运行的是Thread的run方法。
public static void main(String[] args) {
Runnable runnable =new Runnable() {
@Override
public void run() {
System.out.println("runnable的run");
}
};
Thread thread=new Thread(runnable){
@Override
public void run() {
System.out.println("thread的run方法");
}
};
thread.start();
}
通过Callable和FutureTask创建线程
a:创建Callable接口的实现类 ,并实现Call方法
b:创建Callable实现类的实现,使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的Call方法的返回值
c:使用FutureTask对象作为Thread对象的target创建并启动线程
d:调用FutureTask对象的get()来获取子线程执行结束的返回值
public class ThreadDemo03 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Callable<Object> oneCallable = new Tickets<Object>();
FutureTask<Object> oneTask = new FutureTask<Object>(oneCallable);
Thread t = new Thread(oneTask);
System.out.println(Thread.currentThread().getName());
t.start();
}
}
class Tickets<Object> implements Callable<Object>{
//重写call方法
@Override
public Object call() throws Exception {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName()+"-->我是通过实现Callable接口通过FutureTask包装器来实现的线程");
return null;
}
}
Interface Callable
这是一个功能界面,因此可以用作lambda表达式或方法引用的赋值对象。
Callable接口类似于Runnable ,因为它们都是为其实例可能由另一个线程执行的类设计的。 然而,A Runnable不返回结果,也不能抛出被检查的异常。
该Executors类包含的实用方法,从其他普通形式转换为Callable类。
通过线程池创建线程
public class ThreadDemo05{
private static int POOL_NUM = 10; //线程池数量
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ExecutorService executorService = Executors.newFixedThreadPool(5);
for(int i = 0; i<POOL_NUM; i++)
{
RunnableThread thread = new RunnableThread();
//Thread.sleep(1000);
executorService.execute(thread);
}
//关闭线程池
executorService.shutdown();
}
}
class RunnableThread implements Runnable
{
@Override
public void run()
{
System.out.println("通过线程池方式创建的线程:" + Thread.currentThread().getName() + " ");
}
}