一:Java多线程编程

本文详细介绍了Java中实现多线程的两种方式:Thread类和Runnable接口。通过实例展示了如何创建和启动线程,以及两者的区别。还提到了Callable接口及其与Runnable的不同,最后讨论了线程的运行状态。
摘要由CSDN通过智能技术生成

1、进程与线程

          进程:一个应用程序的运行就是一个进程

          多进程:表示一个时间段上多个程序依次进行,而在一个时间点上只会有一个进程执行

          线程:在进程的基础之上划分的更小的程序单元,线程式在进程基础上创建并使用的,所以线程依赖于进程。但现成的启用速度要比进程快,所以当使用多线程进行并发处理时,其执行的性能要高于进程。

          注意:进程运行在windows系统之上,线程运行于进程基础之上

在Java中实现多线程,需要一个专门的线程主体类进行线程的执行任务的定义,而这个主体类需要继承特定的父类或者实现特定的接口才可以完成

 

2、第一种多线程实现方式:Thread类实现多线程

          若有一个类继承了Thread类,且覆写Thread类中提供的一个run()方法,就表示此类为线程的主体类,子类覆写的这个方法就叫线程的主方法,所有要执行的功能都应该写在此方法中。

          1)范例:多线程主体类

//定义一个线程主体类
class MyThread extends Thread{
	//定义类的属性
	private String title;
	//实现有参构造
	public MyThread(String title) {
		super();
		this.title = title;
	}
	//覆写线程的run方法
	@Override
	public void run(){//线程的主体方法
		for (int x = 0;x < 10;x++){
			System.out.println(this.title + "运行,x=" + x);
		}
	}
}

          注意:在启动多线程时,run()方法并不能通过实例化对象进行调用,只能使用start()方法进行调度。

          若使用对象实例化调用,代码如下:

package cn.demos;

//定义一个线程主体类
class MyThread extends Thread{
	//定义类的属性
	private String title;
	//实现有参构造
	public MyThread(String title) {
		super();
		this.title = title;
	}
	//覆写线程的run方法
	@Override
	public void run(){//线程的主体方法
		for (int x = 0;x < 10;x++){
			System.out.println(this.title + "运行,x=" + x);
		}
	}
}
public class Demo1 {

	public static void main(String[] args) {
		new MyThread("线程A").run();
		new MyThread("线程B").run();
		new MyThread("线程C").run();

	}
}
结果线程A运行,x=0
线程A运行,x=1
线程A运行,x=2
线程A运行,x=3
线程A运行,x=4
线程A运行,x=5
线程A运行,x=6
线程A运行,x=7
线程A运行,x=8
线程A运行,x=9
线程B运行,x=0
线程B运行,x=1
线程B运行,x=2
线程B运行,x=3
线程B运行,x=4
线程B运行,x=5
线程B运行,x=6
线程B运行,x=7
线程B运行,x=8
线程B运行,x=9
线程C运行,x=0
线程C运行,x=1
线程C运行,x=2
线程C运行,x=3
线程C运行,x=4
线程C运行,x=5
线程C运行,x=6
线程C运行,x=7
线程C运行,x=8
线程C运行,x=9

          可以看到程序结果是按顺序进行,A线程计算完成才进行B线程的计算,随后是C线程,并没有实现交替进行的结果。所以,启动多线程时,需要使用start()方法来进行调用。

          多线程启动:使用start()方法,代码如下

package cn.demos;

//定义一个线程主体类
class MyThread extends Thread{
	//定义类的属性
	private String title;
	//实现有参构造
	public MyThread(String title) {
		super();
		this.title = title;
	}
	//覆写线程的run方法
	@Override
	public void run(){//线程的主体方法
		for (int x = 0;x < 10;x++){
			System.out.println(this.title + "运行,x=" + x);
		}
	}
}
public class Demo1 {

	public static void main(String[] args) {
		new MyThread("线程A").start();
		new MyThread("线程B").start();
		new MyThread("线程C").start();

	}
}
结果线程A运行,x=0
线程A运行,x=1
线程A运行,x=2
线程C运行,x=0
线程B运行,x=0
线程B运行,x=1
线程B运行,x=2
线程B运行,x=3
线程B运行,x=4
线程B运行,x=5
线程B运行,x=6
线程B运行,x=7
线程B运行,x=8
线程B运行,x=9
线程C运行,x=1
线程A运行,x=3
线程C运行,x=2
线程A运行,x=4
线程A运行,x=5
线程A运行,x=6
线程A运行,x=7
线程A运行,x=8
线程A运行,x=9
线程C运行,x=3
线程C运行,x=4
线程C运行,x=5
线程C运行,x=6
线程C运行,x=7
线程C运行,x=8
线程C运行,x=9

          如上结果交替显示,表示线程启动成功,执行顺序不可控。

          每一个线程对象只能启动一次,若重复启动会报以下错误:

问题主要代码信息
重复启动

public static void main(String[] args) {
        MyThread mt = new MyThread("线程A");
        mt.start();
        mt.start();//重复启动

    }

Exception in thread "main" 

java.lang.IllegalThreadStateException

 

3、第二种多线程实现方式:Runnable接口实现多线程

          1)范例:定义并启动多线程(通常方法)

package cn.demos;

//定义一个线程主体类
class MyThread implements Runnable {
	// 定义类的属性
	private String title;

	// 实现有参构造
	public MyThread(String title) {
		super();
		this.title = title;
	}

	// 覆写线程的run方法
	@Override
	public void run() {// 线程的主体方法
		for (int x = 0; x < 10; x++) {
			System.out.println(this.title + "运行,x=" + x);
		}
	}
}

public class Demo1 {

	public static void main(String[] args) {
		// 启动多线程
		Thread threadA = new Thread(new MyThread("线程AAAA"));
		Thread threadB = new Thread(new MyThread("线程BBBB"));
		Thread threadC = new Thread(new MyThread("线程CCCC"));
		// 启动多线程
		threadA.start();
		threadB.start();
		threadC.start();

	}
}

          

结果线程AAAA运行,x=0
线程BBBB运行,x=0
线程CCCC运行,x=0
线程BBBB运行,x=1
线程BBBB运行,x=2
线程BBBB运行,x=3
线程BBBB运行,x=4
线程AAAA运行,x=1
线程CCCC运行,x=1
线程AAAA运行,x=2
线程CCCC运行,x=2
线程CCCC运行,x=3
线程CCCC运行,x=4
线程AAAA运行,x=3
线程AAAA运行,x=4

          通过文档,观察Runnable接口定义,从之前的学习中可以得知@FunctionalInterface表示函数编程,即Lamda表达式编程,下面的例子将使用Lamda表达式进行启动线程

Runnable接口定义

@FunctionalInterface   //表示函数式接口
public interface Runnable{
    public void run();

}

          2)范例:定义并启动多线程(Lamda表达式启动)

代码结果

public class Demo1 {

    public static void main(String[] args) {
        for (int x = 0; x < 3; x++) { // 定义线程个数
            String title = "线程对象-" + x;
            // 进行对象接收
            Runnable run = () -> {
                for (int y = 0; y < 5; y++) {
                    System.out.println(title + "运行,y=" + y);
                }
            };
            // 启动线程
            new Thread(run).start();
        }
    }
}

线程对象-0运行,y=0
线程对象-2运行,y=0
线程对象-2运行,y=1
线程对象-2运行,y=2
线程对象-2运行,y=3
线程对象-2运行,y=4
线程对象-1运行,y=0
线程对象-0运行,y=1
线程对象-1运行,y=1
线程对象-0运行,y=2
线程对象-0运行,y=3
线程对象-0运行,y=4
线程对象-1运行,y=2
线程对象-1运行,y=3
线程对象-1运行,y=4

 

4、Thread与Runnable关系

 Thread类Runnable接口
定义
public class Thread extends Object implements Runnable

public interface Runnable{
    public void run();

}

代码

package cn.demos;

//定义一个线程主体类
class MyThread extends Thread{
    //定义类的属性
    private String title;
    //实现有参构造
    public MyThread(String title) {
        super();
        this.title = title;
    }
    //覆写线程的run方法
    @Override
    public void run(){//线程的主体方法
        for (int x = 0;x < 10;x++){
            System.out.println(this.title + "运行,x=" + x);
        }
    }
}
public class Demo1 {

    public static void main(String[] args) {
        new MyThread("线程A").start();
        new MyThread("线程B").start();
        new MyThread("线程C").start();

    }
}
 

public class Demo1 {

    public static void main(String[] args) {
        for (int x = 0; x < 3; x++) { // 定义线程个数
            String title = "线程对象-" + x;
            // 进行对象接收
            Runnable run = () -> {
                for (int y = 0; y < 5; y++) {
                    System.out.println(title + "运行,y=" + y);
                }
            };
            // 启动线程
            new Thread(run).start();
        }
    }
}

 
联系

1、从定义中可以看出,Thread类实现了Runnable接口,即Thread类属于Runnable的子类

2、Runnable接口里只定义了一个run()方法,所以,Thread类中的run()方法是覆写的Runnable里的

3、线程对象先调用Thread类中的start()方法,然后start()方法里调用run()方法,具体可看源码

          1)范例:利用卖票程序来实现多个线程的资源并发访问

代码结果

package cn.demos;

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

    private int ticket = 5;

    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            if (this.ticket > 0) {
                // 成功卖出去一张票减少一张
                System.out.println("卖票,ticket=" + this.ticket--);
            }

        }
    }
}

public class Demo1 {

    public static void main(String[] args) {

        MyThread mt = new MyThread();
        new Thread(mt).start();// 第一个线程启动
        new Thread(mt).start();// 第二个线程启动
        new Thread(mt).start();// 第三个线程启动

    }
}
 

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

 

5、Callable接口实现多线程

          在JDK1.5之后,新增了一个新的线程实现接口,解决了Runnable接口没有返回值的问题

 CallableFutureTaskRunnableFutureRunnableFuture
定义
@FunctionalInterface
public interface Callable<V>{
   public V call​() throws Exception;
}
public class FutureTask<V> extends Object implements RunnableFuture<V>public interface RunnableFuture<V> extends Runnable, Future<V>

public interface Runnable{
    public void run();

}

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

//get实现返回值接收
}

FutureTask
public class FutureTask<V>  extends Object  implements RunnableFuture<V>{
      public FutureTask​(Callable<V> callable);
}

          由定义可以看出,Callable定义的时候可以设置一个泛型,此泛型的类型就是返回的数据类型,如此可以避免向下转型带来的安全隐患。

          关系图:

         2)范例:使用Callable实现多线程处理

代码结果

package cn.demos;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

class MyThread implements Callable<String> {
    @Override
    public String call() throws Exception {
        for (int x = 0; x < 5; x++) {
            System.out.println("线程执行,x=" + x);
        }
        return "线程执行完毕";
    }
}
public class Demo1 {
    public static void main(String[] args) throws Exception {
        
        FutureTask<String> task = new FutureTask<>(new MyThread());
        new Thread(task).start();
        System.out.println("线程返回数据!!!!" + task.get());

    }
}
 

线程执行,x=0
线程执行,x=1
线程执行,x=2
线程执行,x=3
线程执行,x=4
线程返回数据!!!!线程执行完毕

          面试题:Runnable与Callable的区别

区别RunnableCallable
1提出时间是JDK1.0提出时间是JDK1.5
2java.lang.Runnable接口之中只提供有一个run()方法,且没有返回值java.util.concurrent.Callable接口中提供有call()方法,可以有返回值

          总结:不论使用何种方法实现多线程,线程的启动只有一个方法,即Thread类的对象.start()方法

6、多线程运行状态

          a.任何一个线程的对象都会使用Thread类进行封装,而线程的启动时调用start(),所以,当对象调用start()方法后,线程其实并没有执行,而是进入了一种就绪状态;

          b.进入就绪状态之后需要等待进行资源调度,当某一个线程调度成功之后则进入运行状态(run()方法);

          c.线程并不会一直运行,中间需要产生一些暂停状态,例如:某个线程执行一段时间之后需要让出资源(或理解为资源被其他线程抢占),此时就会进入堵塞状态,随后会重新回归到就绪状态;

          d.当run()方法执行完毕之后,实际上该线程的主要任务也结束了,那么此时直接进入停止状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值