创建线程的方式有四种:
1.继承Thread类
2.实现Runnable接口
3.实现Callable接口
4.使用线程池
在这四种方法中,根据《Effective Java》一书中指出优先使用线程池
在继承Thread类和实现Runnable接口两种方法中,使用Runnable接口更加推荐(不会有单继承局限的问题)
若想要线程最终返回结果,使用实现Callable接口(只有这种方式有返回结果)
1.继承Thread类创建线程
新建一个线程最简单的方法就是直接继承Thread类,而后覆写该类中的run()方法,但是开启多线程还是要调用start()方法
(1)新建一个线程,使用run方法
package Practise;
class MyThread extends Thread//线程主类
{
private String title;
public MyThread(String title)
{
this.title=title;
}
public void run() {//所有线程从这里开始执行
for(int i=0;i<10;i++)
{
System.out.println(this.title+",i="+i);
}
}
}
public class Test1
{
public static void main(String[] args) {
MyThread myThread1=new MyThread("thread1");
MyThread myThread2=new MyThread("thread2");
MyThread myThread3=new MyThread("thread3");
myThread1.run();
myThread2.run();
myThread3.run();
}
}
(2)启动多线程是使用start方法,不是run方法
public class Test1
{
public static void main(String[] args) {
MyThread myThread1=new MyThread("线程1");
MyThread myThread2=new MyThread("线程2");
MyThread myThread3=new MyThread("线程3");
myThread1.start();
myThread2.start();
myThread3.start();
}
}
多线程每次执行的结果都不同
2.实现Runnable()接口创建线程
使用Thread类创建线程会产生单继承局限的问题,因此实现Runnable接口就很好的解决了这个问题,但是新的问题又产生了,在Runnable接口中没有start()方法,那么创建的线程就没有办法来启动,但是Thread类中的构造方法又很好的解决了这个问题
public Thread(Runnable target)
(1)利用Runnable接口来实现线程主体类
class MyThread implements Runnable{
private String title;
public MyThread(String title ) {
// TODO Auto-generated constructor stub
this.title=title;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<10;i++)
{
System.out.println(this.title+",i="+i);
}
}
}
public class Test1
{
public static void main(String[] args) {
MyThread myThread1=new MyThread("线程1");
MyThread myThread2=new MyThread("线程2");
MyThread myThread3=new MyThread("线程3");
new Thread(myThread1).start();
new Thread(myThread2).start();
new Thread(myThread3).start();//启动多线程
}
}
(2)使用匿名内部类进行Runnable对象创建
public class Test1
{
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Hello World");
}
}).start();
}
}
(3)使用Lamdba表达式进行Runnable对象创建
public class Test1
{
public static void main(String[] args) {
Runnable runnable=()->System.out.println("Hello world");
new Thread(runnable).start();
}
}
Thread与Runnable区别
1)Runnable实现多线程可以避免单继承局限
2)在定义Thread类时,Thread类是Runnable的接口子类,因此在实现Thread类时,一定覆写了Runnable接口的run()方法
3)使用Runnable实现的多线程的程序类可以更好的描述出程序共享的概念(Thread也可以,只不过Runnable更好一些)
线程类的继承结构——代理模式
使用Thread实现数据共享
启动三个线程实现卖票处理。结果变为了卖各自的票
class MyThread extends Thread {
private int ticket = 10;
public void run() {
while (this.ticket > 0) {
System.out.println("剩余票数" + this.ticket--);
}
}
}
public class Test1 {
public static void main(String[] args) {
System.out.println("用Thread实现数据共享");
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
}
而使用Runnable实现共享
Runnable实现的多线程的程序类可以更好的描述出程序共享的概念
class MyThread2 implements Runnable
{
private int ticket=10;
public void run() {
while(this.ticket>0)
{
System.out.println("剩余票数"+this.ticket--);
}
}
}
public class Test1
{
public static void main(String[] args) {
System.out.println("使用Runnable实现共享");
MyThread2 mThread2=new MyThread2();
new Thread(mThread2).start();
new Thread(mThread2).start();
}
}
3.实现Callable接口创建线程
由于Runnable中的run()方法没有返回值,它的设计也遵循了主方法的设计原则:线程开始了就别回头。但是很多时候需要一些返回值,例如某些线程执行完成后可能带来一些返回结果,这种情况下就只能利用Callable来实现多线程
使用Callable定义线程主体类
package Practise;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
class MyThread implements Callable<String>
{
private int ticket=10;
public String call() throws Exception
{
while(this.ticket>0)
{
System.out.println("剩余票数"+this.ticket--);
}
return "票卖完了";
}
}
public class Test1
{
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<String> task=new FutureTask<>(new MyThread());
new Thread(task).start();
new Thread(task).start();
System.out.println(task.get());
}
}
4.使用线程池创建线程
线程池是指多个线程封装在一起的操作
线程池相关操作都在java.util.concurrent包中进行
(1)核心接口(两个)及创建线程池的方法(4个)
1)普通线程池:
public interface ExecutorService extends Executor
--创建无大小限制的线程池
public static ExecutorService newCachedThreadPool()
--创建固定大小的线程池
public static ExecutorService newFixedThreadPool(int nThreads)
--单线程池
public static ExecutorService newSingleThreadExecutor()
2)调度线程池:
public interface ScheduledExecutorService extends ExecutorService
--创建定时调度池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
(2)使用线程池的好处:
多个线程按照组的模式进行程序的处理,这样在逻辑复杂的环境下性能会有很好的提升
创建与大小限制的线程池
public class Test {
public static void main(String[] args) {
// 创建了一个线程池模型,现在里面没有线程
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
int index = i; // 保存i的值
newCachedThreadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " ,i = " + index);
} // 执行线程操作
});
}
newCachedThreadPool.shutdown(); // 关闭线程池
}
}
创建单线程线程池
public class Test {
public static void main(String[] args) {
// 创建了一个线程池模型,现在里面没有线程
// 单线程对象线程池
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
int index = i; // 保存i的值
newSingleThreadExecutor.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " ,i = " + index);
} // 执行线程操作
});
}
newSingleThreadExecutor.shutdown(); // 关闭线程池
}
}
创建固定大小线程池
public class Test {
public static void main(String[] args) {
// 创建了一个线程池模型,现在里面没有线程
// 创建只有固定线程数量的线程池
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
int index = i; // 保存i的值
newFixedThreadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " ,i = " + index);
} // 执行线程操作
});
}
newFixedThreadPool.shutdown(); // 关闭线程池
}
}
创建定时调度池
public class Test {
public static void main(String[] args) {
// 创建一个具备两个线程大小的定时调度线程池
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2);
for (int i = 0; i < 10; i++) {
int index = i;
// 表示3s后开始执行,每间隔2s执行一次
newScheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " , i = " + index);
}
}, 3, 2, TimeUnit.SECONDS);
}
}
}