欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java 多线程,创建线程的3种方式、设计模式-静态代理模式、Lamda表达式

程序员文章站 2022-03-30 22:55:21
...

概念

  • 线程就是独立的执行路径;在程序运行时,即使没有自己创建程,后台也会有多个线程,如主线程,gc线程(垃圾回收线程)
  • main()称之为主线程,为系统的入口,用于执行整个程序;
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
  • 线程会带来额外的开销,如cpu调度时间,并发控制开销。
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

创建线程

创建线程主要有3种方式:

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口

继承thread类

自定义线程类继承Thread类
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
Java 多线程,创建线程的3种方式、设计模式-静态代理模式、Lamda表达式
如上图,run方法就是普通的方法调用,调用start方法是开启线程。
线程开启不一定立即执行,是由CPU调度执行。

//自定义线程类继承自Thread
private static class threadTest extends Thread{
    @Override
    public void run() {
        super.run();
        //重写run()方法,编写线程执行体
        for (int i = 0; i < 20; i++) {
            System.out.println("多线程");
        }
    }

    public static void main(String[] args) {
        //创建一个线程对象
        threadTest tt = new threadTest();
        //调用start方法开启线程
        tt.start();

        for (int i = 0; i < 2000; i++) {
            System.out.println("主线程"+i);
        }
    }
}

实现Runnable接口

定义MyRunnable类,实现Runnable接口
实现run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程

    //定义MyRunnable类实现Runnable接口
    private static class threadTest implements Runnable{
        //实现run()方法,编写线程执行体
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println("多线程");
            }
        }

        public static void main(String[] args) {
            //创建线程对象,调用start()方法启动线程
            threadTest tt = new threadTest();
            //正常的写法
//            Thread thread = new Thread(tt);
//            thread.start();
            new Thread(tt).start();//简化的写法
        }
    }

更推荐使用实现Runnable接口来创建线程,因为Thread也是实现了Runnable接口,使用Runnable可以避免单继承局限性,灵活方便。
而且方便同一个对象被多个线程使用,下面是一个例子:

//创建线程对象,调用start()方法启动线程
threadTest tt = new threadTest();
new Thread(tt,"黄牛1").start();//不同的线程,同一个资源
new Thread(tt,"黄牛2").start();//不同的线程,同一个资源
new Thread(tt,"黄牛3").start();//不同的线程,同一个资源

为什么Runnable要通过Thread来启动线程呢,这就要了解一下静态代理模式了。

设计模式-静态代理模式

真实对象和代理对象都要实现同一个接口。
代理要代理真实角色,代理对象可以做真实对象做不了的事情,真实对象专注做自己的事情。
就像下面这个例子,婚礼新人You和婚礼代理公司WeddingCompany都继承了结婚marry这个接口,各自实现了HappyMarry()抽象方法,最后使用的时候,是将You对象传入WeddingCompany对象中,通过WeddingCompany的HappyMarry()方法完成的。

public static void main(String [] args){
    //三对情侣结婚
    new WeddingCompany(new You("A")).HappyMarry();
    new WeddingCompany(new You("B")).HappyMarry();
    new WeddingCompany(new You("C")).HappyMarry();
}
//结婚这件事情
private interface marry{
    void HappyMarry();
}
//个人的结婚(穿戴好、执行婚礼仪式)
private static class You implements marry{
    private String name;
    public You(String n){
        this.name = n;
    }
    @Override
    public void HappyMarry() {
        System.out.println(this.name+"新人结婚");
    }
}
//婚礼代理公司的结婚(代理新人布置婚礼)
private static class WeddingCompany implements marry{
    private marry target;
    public  WeddingCompany(marry target){
        this.target = target;
    }
    @Override
    public void HappyMarry() {
        System.out.println("收钱");
        System.out.println("布置婚礼现场,主持婚礼");
        this.target.HappyMarry();
        System.out.println("收尾款\n");
    }
}

运行结果:
Java 多线程,创建线程的3种方式、设计模式-静态代理模式、Lamda表达式
我们再看回Runnable,Thread和下面的threadTest都是实现了接口Runnable的类,Thread对象就是作为了threadTest的代理对象来使用的,有了这个代理对象,我们定义一个实现Runnable的类(比如说threadTest)的时候只要考虑最重要的事情(线程中要执行什么)来重写run方法,其他的事情用Thread代理对象来帮我们做。

        public static void main(String[] args) {
            //创建线程对象,调用start()方法启动线程
            threadTest tt = new threadTest();
            //正常的写法
//            Thread thread = new Thread(tt);
//            thread.start();
            new Thread(tt).start();//简化的写法
        }

Runnable还有更简化的写法,需要用到Lamda表达式:

Lamda表达式

Lambda 表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。
Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
例子:
最通常的使用接口的方法:

public class Test {
	public static void main(String [] args) {
	    LamdaTest t = new Test1();//创建外部类对象(父类引用指向子类对象)
	    t.Lamda();//调用方法
	}
}
//实现类
class Test1 implements LamdaTest{
    @Override
    public void Lamda() {
        System.out.println("我是外部类");
    }
}
//定义一个函数式接口(只有一个抽象方法)
interface LamdaTest{
    void Lamda();
}

也可以放到类里,作为静态内部类:

public class Test {
    public static void main(String [] args) {
        LamdaTest t = new Test1();//创建外部类对象(父类引用指向子类对象)
        t.Lamda();//调用方法
    }
    //实现类(静态内部类)
    private static class Test1 implements LamdaTest{
        @Override
        public void Lamda() {
            System.out.println("我是静态内部类");
        }
    }
}    
//定义一个函数式接口
interface LamdaTest{
    void Lamda();
}

也可以把类实现在方法内部,变成局部内部类:

public static void main(String [] args) {
    //实现类(局部内部类)
    class Test1 implements LamdaTest{
        @Override
        public void Lamda() {
            System.out.println("我是局部内部类");
        }
    }
    LamdaTest t = new Test1();//创建外部类对象(父类引用指向子类对象)
    t.Lamda();//调用方法
}
//定义一个函数式接口
interface LamdaTest{
    void Lamda();
}

也可以直接new接口然后实现方法,没有类名,变成匿名内部类:

public static void main(String [] args) {
    //实现类(匿名内部类)
    LamdaTest t = new LamdaTest(){
        @Override
        public void Lamda() {
            System.out.println("我是匿名内部类");
        }
    };
    t.Lamda();//调用方法
}
//定义一个函数式接口
interface LamdaTest{
    void Lamda();
}

还可以更简化,使用Lamda表达式,是Java8的新特性:

public static void main(String [] args) {
    //实现类
    LamdaTest t = ()->{
            System.out.println("我是Lamda表达式");
    };
    t.Lamda();//调用方法
}
//定义一个函数式接口
interface LamdaTest{
    void Lamda();
}

如果要实现的方法中需要传参,括号中可以省略类型名:

public static void main(String [] args) {
    //实现类(匿名内部类)
    LamdaTest t = (a,b)->{
            System.out.println("我是匿名内部类"+a+b);
    };
    t.Lamda(1,2);//调用方法
}
//定义一个函数式接口
interface LamdaTest{
    void Lamda(int a,int b);
}

Runnable也是一个函数式接口,所以我们可以用Lambda 表达式来简化代码:

new Thread(()->{
    System.out.println("Runnable线程运行"); 
}).start();

实现Callable接口

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行:Future result1 = ser.submit(t1);
  6. 获取结果:boolean r1 = result1.get()
  7. 关闭服务:ser.shutdownNow();
public static void main(String [] args) throws ExecutionException, InterruptedException {
    CallableTest t1 = new CallableTest();
    CallableTest t2 = new CallableTest();
    CallableTest t3 = new CallableTest();
    //创建执行服务,线程池
    ExecutorService es = Executors.newFixedThreadPool(3);
    //提交执行
    Future<Boolean> r1 = es.submit(t1);
    Future<Boolean> r2 = es.submit(t2);
    Future<Boolean> r3 = es.submit(t3);

    //获取结果(call方法的返回值)
    boolean rs1 = r1.get();
    boolean rs2 = r2.get();
    boolean rs3 = r3.get();
    //关闭服务
    es.shutdown();

}

private static class CallableTest implements Callable<Boolean>{
    private int ticketNums = 10;
    //实现call()方法,需要抛出异常
    @Override
    public Boolean call() throws Exception {
        while (true){
            if (ticketNums<=0){
                break;
            }
            Thread.sleep(200);
            System.out.println(Thread.currentThread().getName()+"抢到票"+ticketNums--);
        }
        return true;
    }
}