多线程

多线程

线程的创建方式

实现Runnable接口

public class Test {

    public static void main(String[] args){
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable,"t1");	//第一个参数传入实现了Runnable的类,第二个参数设置线程名
        thread.start();
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i < 10; i++){
            System.out.println("这是分支线程第" + i + "次打印");
        }
    }
}

也可以使用匿名内部类的方式:

public class Test {

    public static void main(String[] args){
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i < 10; i++){
                    System.out.println(i);
                }
            }
        }, "t1");
        thread.start();
    }
}

继承Thread

public class Test {

    public static void main(String[] args){
        MyThread thread = new MyThread();
        thread.start();
    }
}

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("这是分支线程");
    }
}

实现Runnable接口与继承Thread相比有如下几点优势:

1、可以避免单继承带来的局限性

2、任务与线程是分离的,提高了程序的健壮性

3、线程池只能接受Runnable类型的任务,

4、通过创建任务,给线程分配的方式实现多线程,更适合多个线程执行相同任务的场景

Callable

public class Test {

    public static void main(String[] args) {
        FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());
        Thread t1 = new Thread(futureTask);
        t1.start();
    }
}

class MyCallable implements Callable<Integer>{	//
    @Override
    public Integer call() throws Exception {
        System.out.println("这是重写的call方法");
        return 1;
    }
}

与上述两种线程创建方式不同,实现callable接口的线程执行完会产生一个返回值

其中call方法会抛出异常,但是run方法不会

Thread类

常用方法

String getName(); //获取当前线程名称

void start(); //启动线程

static void sleep(long millis, int nanos); //让线程休眠millis毫秒+nanos纳秒(第二个参数可以不填)

void setDaemon(boolean on); //将此线程标记为守护(daemon)线程或用户线程

获取、修改当前线程名

public class Test {

    public static void main(String[] args){
        Thread thread = Thread.currentThread();//获取执行这段代码的当前线程对象
        thread.getName();//获取线程名
        thread.setName("t1");//设置线程名
    }
}

线程的中断

public class Test {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                //循环执行任务
                while (true) {
                    for (int i = 1; i < 10; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + i);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            System.out.println("响应中断,结束线程");
                            return;
                        }
                    }
                }
            }
        });

        thread.start();//启动线程

        Thread.sleep(3000);//main线程先睡3秒

        thread.interrupt();//中断线程,执行catch代码块
                           /* 注:线程会继续执行当前任务,直到执行到以下方法时才会响应中断
                                java.lang.Object#wait()
                                java.lang.Object#wait(long)
                                java.lang.Object#wait(long, int)
                                java.lang.Thread#sleep(long)
                                java.lang.Thread#interrupt()
								java.lang.Thread#interrupted()*/
    }
}

线程安全

同步代码块

格式:

synchronized (object o){
            
}

Java中,每个对象都有一个锁标记

当第一个线程执行到同步代码块时,会占有括号中的这个对象锁

后续线程执行到此处时会先观察,括号中的对象锁是否被占用,如果被占用就排队等待

当线程执行完同步代码块后会自动释放掉占用的括号中的对象锁

特点:

​ 锁的粒度比较小

同步方法

将synchronized关键字加在方法上,格式:

public synchronized void say(){
    
}

表示锁的this,即调用该方法的对象

当线程执行到某对象的say()方法时,该对象的对象锁被占用

synchronized也可以用在静态方法上,表示锁的当前类的class对象(类名.class)

同步代码块和同步方法都是隐式锁(即自动上锁,自动解锁)

Lock显式锁

public static void main(String[] args) {
        //任务对象
        Runnable r = new Runnable() {
            private int i = 1;
            //创建锁对象
            private Lock l = new ReentrantLock();
            @Override
            public void run() {
                while (true){
                    l.lock();//当线程执行到此处时会观察l对象是否被锁住,如果是就排队,否则就将l对象锁住并继续往下执行
                    try {
                        if (i < 10){
                            System.out.print("第" + i + "次");
                            System.out.println(Thread.currentThread().getName() + "正在执行");
                            i++;
                        }else {
                            break;
                        }
                    }finally {
                        l.unlock();//锁的释放要放在finally代码块里,防止占用资源
                    }
                }
            }
        };

        new Thread(r, "t1").start();
        new Thread(r, "t2").start();
        new Thread(r, "t3").start();

}

公平锁与非公平锁

公平锁:按先来后到排队获取锁

非公平锁:同时竞争一把锁

上述三种锁的方式都是非公平锁

公平锁的实现方式:

Lock l = new ReentrantLock(true);//创建lock对象时传入一个参数,默认是false表示非公平锁

线程的状态

New:新建状态,调用start方法之后进入可运行状态

Runnable:可运行状态(未抢夺到CPU时间片而陷入等待的线程,依然属于Runnable状态)

Blocked:阻塞状态(没能进入同步代码块的线程会进入该状态)

Waiting:等待状态(调用了wait方法、Thread.join方法会使线程进入该状态)

Timed_Waiting:超时等待状态(调用了Thread.sleep、wait带时间参数的方法、join带时间参数的方法会进入该状态)

Terminated:结束状态

线程池

缓存线程池

public class Test {

    public static void main(String[] args) {
        /**
         * 缓存线程池.
         * (长度无限制)
         * 执行流程:
         * 1. 判断线程池是否存在空闲线程
         * 2. 存在则使用
         * 3. 不存在,则创建线程 并放入线程池, 然后使用
         */
        ExecutorService service = Executors.newCachedThreadPool();
        //向线程池中 加入 新的任务
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程的名称:"+Thread.currentThread().getName());
            }
        });        
    }
}

定长线程池

public class Test {

    public static void main(String[] args) {

        /**
         * 定长线程池.
         * (长度是指定的数值)
         * 执行流程:
         *      1. 判断线程池是否存在空闲线程
         *      2. 存在则使用
         *      3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
         *      4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
         */
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程的名称:"+Thread.currentThread().getName());
            }
        });
    }
}

单线程线程池

public class Test {

    public static void main(String[] args) {

        /**
         * 单线程线程池.
         * 执行流程:
         * 1. 判断线程池 的那个线程 是否空闲
         * 2. 空闲则使用
         * 4. 不空闲,则等待 池中的单个线程空闲后 使用
         */
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程的名称:"+Thread.currentThread().getName());
            }
        });
    }
}

周期性任务定长线程池

public class Test {

    public static void main(String[] args) {
        /**
         * 周期任务 定长线程池.
         * 执行流程:
         * 1. 判断线程池是否存在空闲线程
         * 2. 存在则使用
         * 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
         * 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
         * *
         周期性任务执行时:
         * 定时执行, 当某个时机触发时, 自动执行某任务
         */
        ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
        /**
         * 定时执行
         * 参数1. runnable类型的任务
         * 参数2. 时长数字
         * 参数3. 时长数字的单位
         */
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("俩人相视一笑~ 嘿嘿嘿");
            }
        },5,TimeUnit.SECONDS);
        /**
         * 周期执行
         * 参数1. runnable类型的任务
         * 参数2. 时长数字(延迟执行的时长)
         * 参数3. 周期时长(每次执行的间隔时间)
         * 参数4. 时长数字的单位
         */
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("俩人相视一笑~ 嘿嘿嘿");
            }
        },5,2,TimeUnit.SECONDS);
    }        
}

Lamda表达式

lamda表达式是一种函数式编程思想

在开发过程中经常遇到这样的情况:

在创建线程时,为了实现run方法,要写一堆与run方法的实现无关的代码(即先要创建对象,再通过对象调用方法)

lamda表达式略去了那些无关的要素,只关注于方法

接口中只有一个抽象方法需要被实现的(不是规定接口只能有一个方法),可以使用lamda表达式

语法格式:

() -> {}

由于只有一个方法需要被实现

所以其中的()表示的是需要被实现的方法后面的括号,有入参就在括号里加上入参

后面的大括号表示需要实现方法的方法体

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值