线程的状态
很多学习的科目都涉及到了线程的概念,在JAVA中也是一样的,线程有五种基本状态,五种状态及其之间的转换关系如下(重点
):
五种状态分别是:创建、就绪、运行、阻塞和终止。
多线程的实现
在JAVA中实现多线程有两种方法:继承Thread类、实现Runnable接口。
继承Thread类
java.lang.Thread类是一个线程的核心类,新建一个线程最直接的方法就是继承Thread类,并且实现该类的run方法。
public class MyThread extends Thread {
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(i);
}
}
}
线程的主体类创建好之后,我们自然而然的就会想到实例化对象然后调用run方法,事实上,我们是不能直接调用run方法。
例如:
public class MyThread extends Thread {
private String st;
public MyThread(String st){
this.st=st;
}
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(i);
}
}
}
public class TestThread {
public static void main(String[] args) {
MyThread myThread1=new MyThread("线程1");
MyThread myThread2=new MyThread("线程2");
MyThread myThread3=new MyThread("线程3");
myThread1.run();
myThread2.run();
myThread3.run();
}
}
结果如下:
我们可以看出来,直接调用run方法结果是按顺序打印的,和多线程没有关系。
正确启动多线程是调用Thread类中的start()方法。
如下:
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();
}
这样结果就是交替出现的。
Runnable接口实现多线程
由于继承Thread类会有单继承局限,所以Java中由提供了Runnable接口。
举个例子,用Runnable实现多线程的范例:
首先实现Runnable接口实现多线程的主体类
public class MyRunnable implements Runnable {
public void run() {
for(int i=0;i<3;i++)
{
System.out.println(i);
}
}
}
那么问题来了,我们要启动多线程,前面说的使用Thread类的start()方法,可是现在我们并没有继承Thread类,那么我们观察Thread类会发现,Thread类中有一个构造函数,可以传Runnable对象,构造方法如下:
public Thread(Runnable target)
那么我们就可以使用如下方式启动多线程:
public class TestRunnable {
public static void main(String[] args) {
MyRunnable myRunnableA=new MyRunnable();
MyRunnable myRunnableB=new MyRunnable();
MyRunnable myRunnableC=new MyRunnable();
new Thread(myRunnableA,"线程A").start();
new Thread(myRunnableB,"线程B").start();
new Thread(myRunnableC,"线程C").start();
}
}
多线程的启动永远都是Thread类的start()方法
此时,Runnable接口对象也可以使用匿名内部类或者lamb表达式来表示,例如:
使用匿名内部类就创建Runnable对象
public static void main(String[] args) {
new Thread(new MyRunnable(),"线程A"){
@Override
public void run() {
System.out.println("线程A");
}
}.start();
new Thread(new MyRunnable(),"线程B") {
@Override
public void run() {
System.out.println("线程B");
}
}.start();
new Thread(new MyRunnable(),"线程C") {
@Override
public void run() {
System.out.println("线程C");
}
}.start();
}
使用lamb表达式创建Runnable对象
public static void main(String[] args) {
Runnable runnable=()-> System.out.println("线程A");
Runnable runnable1=()-> System.out.println("线程B");
Runnable runnable2=()-> System.out.println("线程C");
new Thread(runnable).start();
new Thread(runnable1).start();
new Thread(runnable2).start();
}
Thread与Runnable的区别
首先我们知道实现多线程一个是继承Thread类,一个是实现Runnable接口,那么继承就会有单继承的缺陷,这一点Runnable有效的解决了这一局限性。其次我们观察Thread类会发现Thread类实现了Runnable接口,那么一定覆写了run()方法,如下:
public class Thread implements Runnable
@Override
public void run() {
if (target != null) {
target.run();
}
}
在多线程的处理上使用的就是代理设计模式。除了以上的关系之外,实际上在开发之中使用Runnable还有一个特点:使用Runnable实现的多线程的程序类可以更好的描述出程序共享的概念(并不是说Thread不能)
对于共享性,我们可以同一个例子用两段代码来比较一下:
假设我们现在在模拟一个售票系统,那么我们当然希望所有买票的乘客看到的数据是一样的。
用Thread的共享性观察
public class MyThread extends Thread {
private int ticket=10;
@Override
public void run(){
while (ticket>0){
this.ticket--;
System.out.println("剩余票数:"+this.ticket);
}
}
public static void main(String[] args) {
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
}
用Runnable的共享性观察
public class MyThread implements Runnable {
private int ticket=10;
@Override
public void run(){
while (ticket>0){
this.ticket--;
System.out.println("剩余票数:"+this.ticket);
}
}
public static void main(String[] args) {
MyThread myThread=new MyThread();
new Thread(myThread).start();
new Thread(myThread).start();
new Thread(myThread).start();
}
}
Runnable实现的多线程的程序类可以更好的描述出程序共享的概念。
Callable实现多线程
在jdk1.5以后开始追加了新的开发包“java.util.concurrent”,这个包主要是进行高并发编程使用,包含很多在高并发操作中会使用到的类,这个包里定义有一个新的接口Callable。
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Runnable中的run()方法没有返回值,它的设计也遵循了主方法的设计原则:线程开始了就别回头。但是很多时候需要一些返回值,例如某些线程执行完成后可能带来一些返回结果,这种情况下就只能利用Callable来实现多线程。
还是上面那个例子,我们用Callable实现,代码如下:
public class MyThread implements Callable<String> {
private int ticket=10;
@Override
public String call() throws Exception {
while (ticket>0){
this.ticket--;
System.out.println("剩余票数:"+ticket);
}
return "票卖完啦!!!";
}
public static void main(String[] args) {
MyThread myThread=new MyThread();
FutureTask<String> task=new FutureTask<>(new MyThread());//重点
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
try {
System.out.println(task.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}