一、相对于多进程,多线程的优势如下:
1、 进程之间不能共享数据,线程可以
2、系统需要为新创建的进程重新分配系统资源,而创建线程代价较小。
3、java内置多线程功能,简化编程。
二、线程的创建和启动有三种方式:
2.1、继承Thread类创建线程类
1、定义一个继承Thread类的子类,重写run()方法
2、创建Thread子类的实例。
3、调用start()方法启动线程
public class FirstThread extends Thread{
@Override //重写Thread的run方法
public void run(){
System.out.println("继承Thread");
}
}
public class Test {
public static void main(String[] args) {
FirstThread firstThread = new FirstThread();//线程对象
firstThread.start();
}
}
2.2、实现Runnable接口创建线程类
1、定义Runnable接口的实现类,重写接口的run()方法
2、创建Runnable实现类的实例,以此作为Thread的target对象,所以Thread对象才是真正的线程对象。
3、调用start()方法启动线程
public class FirstRunnable implements Runnable{
@Override //重写Thread的run方法
public void run(){
System.out.println("实现 Runnable 接口");
}
}
public class Test {
public static void main(String[] args) {
FirstRunnable firstRunnable = new FirstRunnable();//firstRunnable作为Thread的target对象
Thread firsrThread =new Thread(firstRunnable);
firsrThread.start();
}
}
2.3、通过Callable和Future创建线程
1、创建Callable接口的实现类,并实现call()方法(作为线程的执行体,有返回值)。
2、创建Callable实现类的实例,并使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
3、将FutureTask对象作为Thread对象的target,创建该Thread对象并启动新线程。
4、调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
public class FirstCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("first Callable");
return "first Callable";
}
}
public class Test {
public static void main(String[] args) {
Callable<String> callable = new FirstCallable();//创建Callable 实例
FutureTask<String> task = new FutureTask<>(callable);//FutureTask包装Callable 对象
Thread thread = new Thread(task);
try {
thread.start();
System.out.println("return: "+ task.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
三、线程三种创建方式的比较
3.1、使用继承Thread类的方式创建多线程时:
优势:
编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
劣势:
线程类已经继承了Thread类,所以不能再继承其他父类。
3.2、采用RUnnable和Callable接口的方式创建多线程时:
优势:
线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
劣势:
编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。
3.3、Runnable和Callable的区别:
1、Callable规定(重写)的方法是call(),Runnable规定(重写)的方法是run()。
2、Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
3、Call方法可以抛出异常,run方法不可以。
4、运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
四、示例
4.1、extends Thread
public class Start1 {
public static void main(String[] args) {
StartThread1 wugui = new StartThread1();
StartThread1 tuzi =new StartThread1();
//设置线程优先级
wugui.setPriority(1);
tuzi.setPriority(10);
//设置线程名称
wugui.setName("乌龟线程");
tuzi.setName("兔子线程");
//启动线程
wugui.start();
tuzi.start();
}
public class StartThread1 extends Thread{
public void run(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName());//打印线程调用的名称
System.out.println(thread.getPriority());//优先级
for(int i=0;i<100;i++){
System.out.println(getName()+"跑到了第"+i+"米");
if ( i==98&&thread.getName().equals("兔子线程")){
System.out.println("兔子睡着了");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
4.2、implements Runnable
public class Test {
public static void main(String[] args) {
FirstRunnable firstRunnable = new FirstRunnable();
Thread tuzi = new Thread(firstRunnable);//将任务对象放入线程对象中
Thread wugui =new Thread(firstRunnable);
tuzi.setName("小兔子线程"); //改变线程名称
wugui.setName("小乌龟线程");
tuzi.setPriority(9);//线程优先级
wugui.setPriority(1);//线程优先级
tuzi.start();
wugui.start();
}
}
public class FirstRunnable implements Runnable{
@Override //重写Thread的run方法
public void run(){
Thread thread = Thread.currentThread();
for ( int i=1;i<=100;i++) {
System.out.println(thread.getName()+"跑到了第"+i+"米");
try {
if ( i==99&&thread.getName().equals("小兔子线程")){
System.out.println("小兔子睡着了----");
Thread.sleep(5000);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}