java多线程

限于喜欢 提交于 2020-07-28 07:58:10

线程实现

如果想要在java之中实现多线程的定义,那么久需要有一个专门的线程主体类进行线程的执行任务的定义,而这个主体类的定义要满足:必须实现特定的接口或者继承特定的父类才可以完成。

Thread类实现多线程

Java里面提供有一个java.lang.Thread的程序类,那么一个类只要继承此类就表示这个类为线程的主体类;并不是说这个类久可以实现多线程处理,还需要覆写Thread类中提供的run()方法,而这个方法就属于线程的主方法。

class MyThread extends Thread{//线程的主体类
    private String title;
    public MyThread(String title){
        this.title=title;
    }

    @Override
    public void run() {//线程的主方法
        for (int x = 0; x < 5; x++) {
            System.out.println(this.title+",x的值:"+x);
        }
    }
}

多线程要执行的功能都应在run()方法中进行定义。要想启动多线程必须使用start()方法。

例如:直接实例化对象调用run()方法

public class ThreadDemo {
    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
线程B,x的值:0
线程B,x的值:1
线程B,x的值:2
线程B,x的值:3
线程B,x的值:4
线程C,x的值:0
线程C,x的值:1
线程C,x的值:2
线程C,x的值:3
线程C,x的值:4

运行结果顺序执行,并没有产生交互。

调用start()方法

public class ThreadDemo {
    public static void main(String[] args) {
        new MyThread("线程A").start();
        new MyThread("线程B").start();
        new MyThread("线程C").start();
    }
}
运行结果将会方法很大的改变:
线程C,x的值:0
线程A,x的值:0
线程B,x的值:0
线程A,x的值:1
线程C,x的值:1
线程A,x的值:2
线程B,x的值:1
线程A,x的值:3
线程C,x的值:2
线程A,x的值:4
线程B,x的值:2
线程B,x的值:3
线程B,x的值:4
线程C,x的值:3
线程C,x的值:4

三个线程对象交互执行,并且每次输出得结果不一样。 通过比较两个程序发现,程序最终运行的都是run()方法。

  • 第一个程序直接运行run()方法,程序顺序执行,程序的执行顺序可控
  • 第二个程序通过start()方法间接执行run()方法,所有线程对象都是交替执行,程序的执行顺序不可控

Runnable接口实现多线程

虽然可以通过Thread类的继承来实现多线程的定义,但是在java程序里面继承只能单继承实现,所以java程序之中又提供了第二种实现多线程的结构,通过实现java.lang.Runnable接口。

class MyThread implements Runnable{//线程的主体类
    private String title;
    public MyThread(String title){
        this.title=title;
    }

    @Override
    public void run() {//线程的主方法
        for (int x = 0; x < 5; x++) {
            System.out.println(this.title+",x的值:"+x);
        }
    }
}

但是此时由于不再继承Threa类,那么此时的Mythread类中也就不能再使用start()方法。如果没有start方法,那么就无法启动多线程。采用Thread类的构造方法启动多线程。

public class ThreadDemo {
    public static void main(String[] args) {
       Thread threadA = new Thread(new MyThread("线程对象A"));
       Thread threadB = new Thread(new MyThread("线程对象B"));
       Thread threadC = new Thread(new MyThread("线程对象C"));
       threadA.start();//启动多线程
       threadB.start();//启动多线程
       threadC.start();//启动多线程
    }
}
运行结果:

线程对象B,x的值:0
线程对象B,x的值:1
线程对象A,x的值:0
线程对象C,x的值:0
线程对象A,x的值:1
线程对象B,x的值:2
线程对象A,x的值:2
线程对象C,x的值:1
线程对象A,x的值:3
线程对象B,x的值:3
线程对象A,x的值:4
线程对象C,x的值:2
线程对象B,x的值:4
线程对象C,x的值:3
线程对象C,x的值:4

利用Lambda表达式实现多线程定义

public class ThreadDemo {
    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
线程对象-1运行,y = 0
线程对象-2运行,y = 1
线程对象-0运行,y = 1
线程对象-2运行,y = 2
线程对象-1运行,y = 1
线程对象-2运行,y = 3
线程对象-0运行,y = 2
线程对象-2运行,y = 4
线程对象-1运行,y = 2
线程对象-0运行,y = 3
线程对象-1运行,y = 3
线程对象-0运行,y = 4
线程对象-1运行,y = 4

对代码进一步优化

public class ThreadDemo {
    public static void main(String[] args) {
        for (int x = 0; x < 3; x++) {
            String title = "线程对象-" +x;
            new Thread(()->{
                for (int y = 0; y < 5; y++) {
                    System.out.println(title+"运行,y = "+y);
                }
            }).start();
        }
    }
}

开发中优先考虑Runnable接口实现多线程

Thread 与 Runnable的关系

经过一系列分析发现,在多线程的实现过程中已经有了两种做法,Thread类、Runnable接口,由于Runnbnable接口可以避免单继承的举行,同时也可以更好地进行程序的扩充,所以使用Runnable接口是最方便的。

打开Thread类 public class Thread implements Runnable{}发现Thread类实现了Runnable接口,并且覆写了接口的run()方法。

多线程开发

例如:卖票程序实现多个线程的资源并发访问

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 ThreadDemo {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        //三个人同时卖票
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}
运行结果:
卖票,ticket= 3
卖票,ticket= 5
卖票,ticket= 4
卖票,ticket= 1
卖票,ticket= 2

Callable实现多线程

传统开发,如果要实现多线程,那么肯定要依靠Runnable,但是Runnable接口在线程执行完毕后无法获取一个返回值,所以JDK1.5之后提出一个新的实现接口:java.util.concurrent.Callable接口

接口定义如下:

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

可以发现Callable定义的时候可以设置一个泛型,此泛型的类型就是返回数据的类型,好处:避免向下转型带来的安全隐患。

Callable

Callable使用

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
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);
        }
        return "线程执行完毕";
    }

}
public class ThreadDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask task = new FutureTask(new MyThread());
        new Thread(task).start();
        System.out.println("线程执行结果:"+task.get());
    }
}
运行结果:
线程执行:0
线程执行:1
线程执行:2
线程执行:3
线程执行:4
线程执行结果:线程执行完毕

Runnable与Callable接口的区别:

  • Runnable是在JDK1.0的时候提出的多线程的实现接口,而Callable是在JDK1.5之后提出的;
  • java.lang.Runnable接口之中只提供一个run()方法,并且没有返回值;
  • java.util.concurrent.Callable接口提供一个call()方法,可以有返回值;

多线程的运行状态

编写程序过程:定义线程主体类,而后通过Thread类进行线程启动,但是并不意味着调用start()方法之后,线程就开始运行,因为线程处理有自己的状态

运行状态图如下:

  1. 任何一个线程的对象都应该使用Thread类进行封装,所以线程的启动使用的是start()方法,但是启动的时候线程将进入一种就绪状态,并没有执行
  2. 进入就绪态自豪和需要等待资源调度,当某一个线程调度成功之后则进入倒运行态(run()方法),但是所有的线程不可能一直持续执行下去,中间需要产生一些暂停的状态,例如:某个线程执行一段时间之后就需要让出资源,而后这个线程就会进入阻塞状态,随后重新回归就绪态
  3. 当run()方法执行完毕之后,实际上该线程的主要任务就结束了,那么此时就可以直接进入到停止状态。

线程常用的操作方法

多线程主要操作方法都在Thread类中定义

线程命名与取得

由于多线程的运行状态不确定,那么程序开发之后为了获取到一些需要的线程,就需要依靠线程的名字进行操作。

Thread类中名称的处理

  • 构造方法:public Thread(Runnable target, String name)
  • 设置名字:public final synchronized void setName(String name)
  • 取得名字:public final String getName()

由于线程的运行状态不可控,所以不能依靠this关键字获取线程,但是每个线程都需要执行run()方法,Thread类中提供获取当前线程的方法:

  • 获取当前线程:public static native Thread currentThread();
class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());//获取当前线程名字
    }
}
public class ThreadDemo {
    public static void main(String[] args)  {
        MyThread mt = new MyThread();
        new Thread(mt,"线程A").start();//设置线程名字
        new Thread(mt).start();                 //未设置线程名字
        new Thread(mt).start();                 //未设置线程名字
        new Thread(mt).start();                 //未设置线程名字
        new Thread(mt,"线程B").start();//设置线程名字
    }
}
运行结果:
线程A
Thread-0
Thread-1
线程B
Thread-2

如果没有设置线程名字,则会自动生成不重复的名字

注意:主方法也是一个线程,每当使用java命令执行程序时候就表示启动了一个JVM进程,一个电脑上可以同时启动若干个JVM进程,每个JVM进程都会有各自的线程。

线程休眠

如果希望线程暂缓执行,就可以执行此方法

在Thread类中的定义

  • 休眠:public static native void sleep(long millis) throws InterruptedException
  • 休眠:public static void sleep(long millis, int nanos) throws InterruptedException

在进行休眠的时候有可能会产生中断异常InterruptedException,该异常属于Exception的子类,所以该异常必须处理

public class ThreadDemo {
    public static void main(String[] args)  {
        new Thread(()->{
            for (int x = 0; x < 10; x++) {
                System.out.println(Thread.currentThread().getName()+",x="+x);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"线程对象").start();
    }
}
运行结果(停顿之后输出):
线程对象,x=0
线程对象,x=1
线程对象,x=2
线程对象,x=3
线程对象,x=4
线程对象,x=5
线程对象,x=6
线程对象,x=7
线程对象,x=8
线程对象,x=9

休眠的主要特点是可以自动实现线程的唤醒,以继续进行后续得处理,但是如果有多个线程对象那么休眠也是有先后顺序的

线程中断

线程休眠可以被其他线程打断。例如,线程休眠中的中断异常

Thread类中的方法:

  • 判断线程是否被中断:public static boolean interrupted()
  • 中断线程执行:public void interrupt()

假设一个线程需要休眠10s,我们在1s后对其进行中断处理

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("线程需要休眠");
            try {
                Thread.sleep(10000);
                System.out.println("线程休眠中!!");
            } catch (InterruptedException e) {
                System.out.println("休眠被打断!!");
            }
        });
        thread.start();
        Thread.sleep(1000);
        if (!thread.interrupted()){//判断线程是否中断
            System.out.println("打断线程");
            thread.interrupt();
        }
    }
}
运行结果:
线程需要休眠
打断线程
休眠被打断!!

线程的强制执行操作

当满足某些条件之后,某一个线程对象可以一直独占资源,一直到该线程执行结束

Thread类中的方法:

  • 强制执行:public final void join() throws InterruptedException
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread mainThread = Thread.currentThread();
        Thread thread = new Thread(() -> {
            for (int x = 0; x < 10; x++) {
                if (x == 1){
                    try {
                        mainThread.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+",x = "+ x);
            }
        },"低级线程");
       thread.start();
        for (int y = 0; y <10 ; y++) {
            Thread.sleep(100);
            System.out.println("高级线程,y = "+ y);
        }
    }
}
运行结果:

高级线程,y = 0
低级线程,x = 0
高级线程,y = 1
高级线程,y = 2
高级线程,y = 3
高级线程,y = 4
高级线程,y = 5
高级线程,y = 6
高级线程,y = 7
高级线程,y = 8
高级线程,y = 9
低级线程,x = 1
低级线程,x = 2
低级线程,x = 3
低级线程,x = 4
低级线程,x = 5
低级线程,x = 6
低级线程,x = 7
低级线程,x = 8
低级线程,x = 9

根据结果可知:高级线程强制执行完毕后,低级线程才开始运行。

注意:在进行线程强制执行的时候一定要获取强制执行的线程对象之后才可以执行join()的调用

线程礼让

先将资源让出去,让别的线程先执行

Thread中的方法:

  • 礼让:public static native void yield()
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int x = 0; x < 10; x++) {
                if (x%2 == 0){
                    Thread.yield();//线程礼让
                    System.out.println("执行线程礼让");
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+",x = "+ x);
            }
        },"低级线程");
       thread.start();
        for (int y = 0; y <10 ; y++) {
            Thread.sleep(100);
            System.out.println("高级线程,y = "+ y);
        }
    }
}
运行结果:
执行线程礼让
高级线程,y = 0
低级线程,x = 0
高级线程,y = 1
低级线程,x = 1
执行线程礼让
高级线程,y = 2
低级线程,x = 2
高级线程,y = 3
低级线程,x = 3
执行线程礼让
高级线程,y = 4
低级线程,x = 4
高级线程,y = 5
低级线程,x = 5
执行线程礼让
高级线程,y = 6
低级线程,x = 6
高级线程,y = 7
低级线程,x = 7
执行线程礼让
高级线程,y = 8
低级线程,x = 8
高级线程,y = 9
低级线程,x = 9

礼让执行每次执行调用yield()方法都只会礼让一次当前的资源。

线程优先级

理论上来讲线程的优先级越高越有可能先执行(越有可能抢占到资源)

Thread类中的方法:

  • 设置优先级:public final void setPriority(int newPriority)
  • 获取优先级:public final int getPriority()

在进行优先级定义的时候都是通过int类型的数字来完成的,而对于此数字的选择在Thread类中有三个常量

  • 最高优先级:public static final int MAX_PRIORITY = 10
  • 中等优先级:public static final int NORM_PRIORITY = 5
  • 最低优先级:public static final int MIN_PRIORITY = 1
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
       Runnable runnable = ()->{
           for (int x = 0; x < 5; x++) {
               try {
                   Thread.sleep(100);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println(Thread.currentThread().getName()+"执行");
           }
       };
       Thread threadA = new Thread(runnable,"线程对象A");
       Thread threadB = new Thread(runnable,"线程对象B");
       Thread threadC = new Thread(runnable,"线程对象C");
       //设置线程优先级
        threadA.setPriority(Thread.MAX_PRIORITY);//设置A为最高
        threadB.setPriority(Thread.MIN_PRIORITY);//设置B为最低
       threadA.start();
       threadB.start();
       threadC.start();
    }
}
运行结果:
线程对象A执行
线程对象B执行
线程对象C执行

线程对象A执行
线程对象C执行
线程对象B执行

线程对象A执行
线程对象C执行
线程对象B执行

线程对象A执行
线程对象C执行
线程对象B执行

线程对象A执行
线程对象C执行
线程对象B执行

根据运行结果:线程A的优先级最高,最优先执行,B的最低,最后执行,但是不是绝对

注意:优先级高的先执行,但是不是绝对先执行

主线程优先级


public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(Thread.currentThread().getPriority());
	System.out.println(new Thread().currentThread().getPriority());
    }
}

运行结果:
5
5

可以发现:主线程是中等优先级,创建的子线程的优先级也是5

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!