Java_04 多线程:Thread,Runnable,Callable,Lambda

多线程

Process进程:是程序的一次执行过程,是系统运行程序的基本单位,一个进程就是一个执行中的程序;

Thread线程(main,gc):与进程类似,是一个比进程更小的执行单位,一个进程中可以执行多个线程,多个线程可以共享同一块空间

线程创建:继承Thread类,实现Runable接口,实现Callable接口

继承Thread类

package com.alpari;

/**
 * 创建线程方式一:继承Thread类,重写run()方法,调用start()方法开启线程
 * 线程开启不一定执行,由CPU调度执行
 */
public class DemoThread  extends Thread{
    @Override
    public void run() {
        // run方法线程体
        for (int i = 0; i < 5; i++) {
            System.out.println("----"+i);
        }
    }

    public static void main(String[] args) {
        // 创建一个线程对象
        DemoThread thread = new DemoThread();
        // 调用start()方法开启线程
        thread.start();
        System.out.println("-------");
    }
}

实现Runnable接口

package com.alpari;

/**
 * 创建线程方式二:实现runnable接口,重写run()方法,调用start()方法开启线程
 * 线程开启不一定执行,由CPU调度执行,避免单继承局限。
 */
public class DemoThread  implements Runnable{
    @Override
    public void run() {
        // run方法线程体
        for (int i = 0; i < 5; i++) {
            System.out.println("----"+i);
        }
    }

    public static void main(String[] args) {
        // 创建一个线程对象
        DemoThread thread = new DemoThread();

        // 创建线程对象,通过线程对象来开启多线程(代理)
        new Thread(thread).start();
    }
}
package com.alpari;

/**
 * 抢票问题:
 * 发现问题:多个线程操作同一个资源的情况下,线程不安全
 */
public class DemoThread  implements Runnable{

    private int ticketNum = 10;
    @Override
    public void run() {
        // run方法线程体
        while (true) {
            if (ticketNum <= 0){
                break;
            }
            // 模拟延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"--"+ ticketNum--);
        }
    }

    public static void main(String[] args) {
        // 创建一个线程对象
        DemoThread thread = new DemoThread();

        // 创建线程对象,通过线程对象来开启多线程(代理)
        new Thread(thread, "q").start();
        new Thread(thread, "w").start();
        new Thread(thread, "e").start();
    }
}
// 输出:
e--10
w--10
q--10
e--9
q--8
w--9
e--7
q--5
w--6
e--4
w--4
q--3
w--2
e--2
q--2
w--1
e--0
q--1

实现Callable接口

package com.alpari;

import java.util.concurrent.*;

/**
 * 创建方式三:实现Callable接口,有返回值
 */
public class DemoThread  implements Callable<String> {

    @Override
    public String call() throws Exception {
        int num = 10;
        for (int i = 0; i < 10; i++) {
            if (i>0) {
                System.out.println("卖票:"+num--);
            }
        }
        return "今日票已卖光";
    }

    public static void main(String[] args) throws Exception {
        // 创建一个线程对象
        DemoThread thread = new DemoThread();

        // 创建执行服务,创建一个线程池 Executors.newFixedThreadPool(1)
        ExecutorService ex = Executors.newFixedThreadPool(1);// 1 代表线程数量

        // 提交执行
        Future<String> submit = ex.submit(thread);

        // 获取结果
        String s = submit.get();

        System.out.println(s);

        // 关闭服务
        ex.shutdown();
    }
}

Lamda表达式

任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口(JDK1.8);对于函数式接口,我们可以通过lambda表达式来创建该接口的对象

package com.alpari;

public class DemoLambda {

    // 3.静态内部类
    static class Like implements ILike{
        @Override
        public void lambda() {
            System.out.println("I like lambda2");
        }
    }

    public static void main(String[] args) {
        ILike like = new ILikeImpl();
        like.lambda();

        Like like2 = new Like();
        like2.lambda();

        // 3.匿名内部类
        new ILike() {
            @Override
            public void lambda() {
                System.out.println("I like lambda3");
            }
        }.lambda();

        // 4.lambda表达式
        like = () -> {
            System.out.println("I like lambda4");
        };
        like.lambda();
    }
}

// 1.定义一个函数式接口
interface ILike {
    void lambda();
}

// 2.实现类
class ILikeImpl implements ILike {
    @Override
    public void lambda() {
        System.out.println("I like lambda");
    }
}

线程状态

停止线程

package com.alpari;

/**
 * 建议线程正常停止:利用次数,不建议死循环
 * 建议使用标志位:设置一个标志位
 * 不要使用stop,destory等过时方法
 */
public class DemoStop implements Runnable{

    // 1.设置一个标识位
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println("run...thread"+i);
        }
    }

    // 2.设置一个方法
    public void stop() {
        this.flag = false;
    }

    public static void main(String[] args) {
        DemoStop stop = new DemoStop();
        new Thread(stop).start();

        for (int i = 0; i < 100; i++) {
            System.out.println("main"+i);
            if (i == 80) {
                // 调用stop方法切换标志位,让线程停止
                stop.stop();
                System.out.println("线程该停止了。。");
            }
        }
    }
}
// 输出:
...
main79
main80
run...thread0
线程该停止了。。
main81
main82
...

线程休眠 sleep

package com.alpari;

import java.text.SimpleDateFormat;
import java.util.Date;

// 模拟倒计时
public class DemoSleep {
    public static void a() {
        int num = 10;
        while (true) {
            try {
                Thread.sleep(1000);
                System.out.println(num--);
                if (num<=0) {
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        // a(); // 调用倒计时方法

        // 打印当前时间
        Date startTime = new Date(System.currentTimeMillis()); // 获取当前时间

        while (true) {
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                startTime = new Date(System.currentTimeMillis()); // 更新当前时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
// 输出:
16:39:31
16:39:32
16:39:33

线程安全问题

package com.alpari;
// 模拟买票
public class UnSafeBuyTicket {

    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();

        new Thread(buyTicket, "a").start();
        new Thread(buyTicket, "b").start();
    }
}
class BuyTicket implements Runnable {

    // 票
    private int ticketNum = 10;
    boolean flag = true; // 外部停止方式
    @Override
    public void run() {
        // 买票
        while (flag) {
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    // public synchronized void buy() 利用synchronized来保证安全,可以锁方法也可以锁个代码块,默认锁的是this当前对象,哪个是变化的就锁哪个就行了。
    
    public void buy() throws InterruptedException {
        // 判断是否有票
        if (ticketNum<=0) {
            flag = false;
            return;
        }
        // 模拟延时
        Thread.sleep(1000);

        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNum--);
    }
}
// a和b都拿到6
b拿到10
a拿到9
a拿到8
b拿到7
b拿到6
a拿到6

优先级Priority

public class MainClass2 {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread());
        MyThread my= new MyThread();
        //通过Thread类的构造方法给线程起名,线程默认名为Thread-N
        Thread t1 = new Thread(my,"线程A");
        Thread t2 = new Thread(my,"线程B");
        Thread t3 = new Thread(my,"线程C");
        //改变线程的优先级,最大为10,最小为1,当线程的优先级较大时,抢占资源的可能性会较大,线程默认优先级是5
        t1.setPriority(Thread.MIN_PRIORITY);
        t2.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        t2.start();
        t3.start();
    }
}

问题

  • sleep和wait的区别:
    • sleep是Thread类的方法,wait是Object类的方法
    • sleep方法不能被唤醒,wait可以被notify和notifyAll唤醒
    • sleep不会释放当前对象的资源锁,wait会释放
  • notify和notifyAll的区别:
    • notify是哪个线程先睡眠,则先唤醒哪个,排队唤醒
    • notifyAll是一起唤醒所有,谁先抢到资源谁先执行
  • 线程的声明周期:
    • 新建--就绪--运行--阻塞--死亡
  • 生产者消费者模式。。。。。
posted @ 2020-12-31 18:16  睡个好觉"  阅读(231)  评论(0)    收藏  举报