java多线程

Process与Thread

  • 程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态概念。
  • 进程是执行程序的一次执行过程,他是一个动态的概念。是系统资源分配的单位。
  • 通常一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然,没有存在的意义。线程是CPU调度和执行的单位。

​ 注意:很多线程是模拟出来的,真正的多线程是指有很多个CPU,即多核,如服务器。如果是模拟出来的多线程,即在一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。


线程的创建

继承Thread类

  • 自定义线程类继承Thread类
  • 重写run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
public class TestThread extends Thread {
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("run---" + i);
        }
    }
    
    public static void main(String[] args) {
        //创建一个线程对象
        TestThread testThread = new TestThread();
        //调用start()方法开启线程
        testThread.start();
        //main线程,主线程
        for (int i = 0; i < 20; i++) {
            System.out.println("main---" + i);
        }
    }
}

不建议使用,避免OOP单继承局限性

实现Runnable接口

  • 自定义类实现Runnable接口
  • 实现run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
public class TestThread implements Runnable {
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("run---" + i);
        }
    }

    public static void main(String[] args) {
        //创建runnable接口实现类对象
        TestThread testThread = new TestThread();
        //创建线程对象,通过线程对象来开启我们的线程,代理
        Thread thread = new Thread(testThread);
        thread.start();
		/*new Thread(testThread).start();*/
        for (int i = 0; i < 20; i++) {
            System.out.println("main---" + i);
        }
    }
}

推荐使用,避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

模拟龟兔赛跑

//模拟龟兔赛跑
public class Race implements Runnable {
    //胜利者
    private static String winner;

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            //模拟兔子休息
            if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //判断比赛是否结束
            boolean flag = gameOver(i);
            if (flag) {
                break;
            }
            System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "步");
        }
    }

    //判断是否完成比赛
    private boolean gameOver(int step) {
        if (winner != null) {
            return true;
        } else {
            if (step >= 100) {
                winner = Thread.currentThread().getName();
                System.out.println("winner is " + winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race, "兔子").start();
        new Thread(race, "乌龟").start();
    }
}

实现Callable接口

import java.util.concurrent.*;
public class TestCallable implements Callable<String> {
    String name;

    public TestCallable(String name) {
        this.name = name;
    }

    @Override
    public String call() throws Exception {
        return name;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1 = new TestCallable("t1");
        TestCallable t2 = new TestCallable("t2");
        TestCallable t3 = new TestCallable("t3");
        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(1);
        //提交执行
        Future<String> r1 = ser.submit(t1);
        Future<String> r2 = ser.submit(t2);
        Future<String> r3 = ser.submit(t3);
        //获取结果
        String res1 = r1.get();
        String res2 = r2.get();
        String res3 = r3.get();
        System.out.println(res1);
        System.out.println(res2);
        System.out.println(res3);
        //关闭服务
        ser.shutdown();
    }
}

静态代理

静态代理模式:

​ 真实对象和代理对象都要事先同一个接口

​ 代理对象要代理真实角色

好处:

​ 代理对象可以做很多真实对象做不了的事情

​ 真实对象专注做自己的事情

下面模拟结婚为例,婚庆公司为代理对象

public class StaticProxy {
    public static void main(String[] args) {
        WeddingCompany weddingCompany = new WeddingCompany(new You());
        weddingCompany.HappyMarry();
    }
}

interface Marry{
    void HappyMarry();
}
//真实角色,你去结婚
class You implements Marry{

    @Override
    public void HappyMarry() {
        System.out.println("要结婚了,超开心!");
    }
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry{
    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        System.out.println("before:布置现场");
        this.target.HappyMarry();
        System.out.println("after:收尾款");
    }
}

Lambda表达式

  • λ希腊字母表中排序第是一位的字母,英文名称为Lambda

  • 避免匿名内部类定义过多

  • 其实质属于函数式编程的概念

(params) -> expression[表达式]

(params) -> statement [语句]

(params) ->

new Thread(()-> System.out.println("Lambda表达式")).start();
  • 函数式解接口的定义:

    • 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。

    • 对于函数式接口,我们可以通过Lambda表达式来创建该接口的对象

/**
 * 推导Lambda表达式
 */
public class TestLambda {
    //3.静态内部类
    static class Like2 implements ILike {

        @Override
        public void lambda() {
            System.out.println("I Like Lambda2!");
        }
    }

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

        like = new Like2();
        like.lambda();

        //4.局部内部类
        class Like3 implements ILike {
            @Override
            public void lambda() {
                System.out.println("I Like Lambda3!");
            }
        }
        like = new Like3();
        like.lambda();

        //5.匿名内部类
        like = new ILike() {
            @Override
            public void lambda() {
                System.out.println("I Like Lambda4!");
            }
        };
        like.lambda();

        //6.Lambda表达式,参数只有一个可以去除小括号,代码语句只有一条可以去除花括号
        like = () -> {
            System.out.println("I Like Lambda5!");
        };
        like.lambda();
    }
}

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

//2.实现类
class Like implements ILike {

    @Override
    public void lambda() {
        System.out.println("I Like Lambda!");
    }
}

线程

线程状态

线程方法

方法 说明
setPriority(int newPriority) 更改线程的优先级
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
void join() 等待该线程终止
static void yield() 暂停当前正在执行的线程对象,并执行其他线程
void interrupt() 中断线程,别用这个方式
boolean isAlive() 测试线程是否处于活动状态

停止线程

  • 不推荐使用JDK提供的stop()、destroy()方法 [已废弃]
  • 推荐线程自己停止下来
  • 建议使用一个标志位进行终止变量当flag=false,则终止线程运行
public class TestStop implements Runnable {
    private boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            System.out.println("run... Thread");
        }
    }

    public void stop() {
        this.flag = false;
    }

    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        for (int i = 0; i < 902; i++) {
            System.out.println("main" + i);
            if (i == 900) {
                testStop.stop();
                System.out.println("线程该停止了!");
            }
        }
    }
}

线程休眠

  • sleep(时间)指定当前线程阻塞的毫秒数

  • sleep存在异常InterruptedException

  • sleep时间达到后线程进入就绪状态

  • sleep可以模拟网络延时,倒计时等

  • 每个对象都有一个锁,sleep不会释放锁

      
    //买票
    public class BuyTick implements Runnable {
        //票数
        private int ticketNums = 10;
    
        @Override
        public void run() {
            while (true) {
                if(ticketNums <= 0){
                    break;
                }
                //模拟延时:放大问题的发生性
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " --> 拿到了第" + ticketNums-- + "票");
            }
        }
    
        public static void main(String[] args) {
            BuyTick ticket = new BuyTick();
            new Thread(ticket,"小明").start();
            new Thread(ticket,"老师").start();
            new Thread(ticket,"黄牛党").start();
        }
    }
    //当前是线程不安全的
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    public class TestSleep {
        public static void main(String[] args) throws InterruptedException {
            //tenDown();
    
            //打印当前系统时间
            Date startTime = new Date(System.currentTimeMillis());//获取当前系统时间
            while (true){
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                startTime = new Date(System.currentTimeMillis());//更新当前时间
            }
        }
    
        public static void tenDown() throws InterruptedException {//模拟倒计时
            int num = 10;
            while (true){
                Thread.sleep(1000);
                System.out.println(num--);
                if(num <= 0){
                    break;
                }
            }
        }
    }
    
    

线程礼让

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转化为就绪状态
  • 让CPU重新调度,礼让不一定成功!看CPU心情
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield, "线程1").start();
        new Thread(myYield, "线程2").start();
    }
}
class MyYield implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "start");
        Thread.yield();//线程礼让
        System.out.println(Thread.currentThread().getName() + "end");
    }
}

线程的强制执行

  • Join合并线程,带此线程执行完成后,再次执行其他线程,其他线程阻塞
  • 可以想象成插队
public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("线程VIP来了 " + i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //启动我们的线程
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();
        //主线程
        for (int i = 0; i < 205; i++) {
            if (i == 200) {
                thread.join();
            }
            System.out.println("main " + i);
        }
    }
}

线程的状态获取

Thread thread = new Thread();
//观察状态
Thread.State  state = thread.getState();
System.out.println(state);//NEW
thread.start();//启动线程
state = thread.getState();
System.out.println(state);//RUNNABLE
while(state != Thread.State.TERMINATED){//只要线程不终止就一直输出状态
    Thread.sleep(100);
    state = thread.getState();//更新状态
    System.out.println(state);//输出状态
}

线程的优先级

  • Java提供一个线程调度器来监控程序启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
  • 线程的优先级用数字表示,范围从1~10.
    • Thread.MIN_PRIORITY = 1;
    • Thread.MAX_PRIORITY = 10;
    • Thread.NORM_PRIORITY = 5;
  • 使用getPriority()/setPriority(int xxx)方法获取或改变优先级

注意先设置优先级在启动!

优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看CPU的调度

守护线程

  • 线程分为用户线程守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如后台记录操作日志、监控内存、垃圾回收等待
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        Me me = new Me();
        Thread thread = new Thread(god);
        thread.setDaemon(true);//默认是false表示是用户线程,正常的线程都是用户线程

        thread.start();//上帝守护线程启动

        new Thread(me).start();//用户线程启动了
    }
}
class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("上帝保佑着你!");
        }
    }
}

class Me implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("我一生都开心的活着");
        }
        System.out.println("============goodbye!world!");
    }
}

线程同步

  • 并发:同一个对象多个线程 同时操作
  • 形成条件: 队列和锁
  • synchronized

同步方法及同步代码块

同步方法

//买票
public class BuyTick {

    public static void main(String[] args) {
        Buy ticket = new Buy();
        new Thread(ticket, "小明").start();
        new Thread(ticket, "黄牛党").start();
    }

}
class Buy implements Runnable{
    //票数
    private int ticketNums = 100;
    private boolean flag = true;//外部停止方式

    @Override
    public void run() {
        while (flag) {
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //synchronized同步方法,锁的是this
    private synchronized void buy() throws InterruptedException {
        //判断是否还有票
        if (ticketNums <= 0) {
            flag = false;
        }
        //买票
        System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
    }
}

同步代码块

//买票
public class BuyTick {

    public static void main(String[] args) {
        Buy ticket = new Buy();
        new Thread(ticket, "小明").start();
        new Thread(ticket, "黄牛党").start();
    }

}

class Buy implements Runnable {
    //票数
    private int ticketNums = 100;
    private boolean flag = true;//外部停止方式

    @Override
    public void run() {
        //同步代码块
        synchronized (this) {
            while (flag) {
                try {
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    private void buy() throws InterruptedException {
        //判断是否还有票
        if (ticketNums <= 0) {
            flag = false;
        }
        //买票
        System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
    }
}

CopyOnWriteArrayList

import java.util.concurrent.CopyOnWriteArrayList;
public class TestJUC {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(3000);

        System.out.println(list.size());
    }
}

死锁

两个线程抢同一个进程,比如一双筷子,你拿了一只,他拿了一只,都在等在对方把剩下哪只给过来,不过谁都不愿意给谁。

public class DeadLock {
    public static void main(String[] args) {
        Makeup girl1 = new Makeup(0,"灰姑娘");
        Makeup girl2 = new Makeup(1,"白雪公主");
        girl1.start();
        girl2.start();
    }
}
//口红
class Lipstick {
}
//镜子
class Mirror {
}
class Makeup extends Thread {
    //需要的资源,只有一份,用static保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;//选择
    String girlName;//使用化妆品的人

    Makeup(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //化妆,互相持有对方的锁,就是需要拿到对方的资源
    private void makeup() throws InterruptedException {
        if (choice == 0){
            synchronized (lipstick){//获得口红的锁
                System.out.println(this.girlName + "获得口红的锁!");
                Thread.sleep(1000);
                synchronized (mirror){//一秒钟后想获得镜子的锁
                    System.out.println(this.girlName + "获得镜子的锁!");
                }
            }
        }else {
            synchronized (mirror){//获得镜子的锁
                System.out.println(this.girlName + "获得镜子的锁!");
                Thread.sleep(2000);
                synchronized (lipstick){//一秒钟后想获得口红的锁
                    System.out.println(this.girlName + "获得口红的锁!");
                }
            }
        }
    }
}

Lock锁

import javax.sound.midi.Soundbank;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}
class TestLock2 implements Runnable {
    int ticketNums = 10;
    //定义lock锁
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();//加锁
                if (ticketNums > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNums--);
                } else {
                    break;
                }
            } finally {
                lock.unlock();//解锁
            }
        }
    }
}

线程协作

方法名 作用
wait() 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
wait(long timeout) 指定等待的毫秒数
notify() 唤醒一个处于等待状态的下线程
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

均是Object类的方法,都只能在同步方法或同步代码块中使用,否者会抛出异常

管程法

//测试:生产者消费者模型 --> 利用缓冲区解决 : 管程法
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }
}

//生产者
class Productor extends Thread {
    SynContainer container;

    public Productor(SynContainer container) {
        this.container = container;
    }

    //生产

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Item(i));
            System.out.println("生产了" + i + "产品");

        }
    }
}

//消费者
class Consumer extends Thread {
    SynContainer container;

    public Consumer(SynContainer container) {
        this.container = container;
    }
    //消费

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了第" + container.pop().id + "产品");
        }
    }
}

//产品
class Item {
    int id;//产品编号

    public Item(int id) {
        this.id = id;
    }
}

//缓冲区
class SynContainer {
    //需要一个容器大小
    Item[] items = new Item[10];
    int count = 0;

    //生产者放入产品
    public synchronized void push(Item item) {
        //如果容器满了,就需要等待消费者消费
        if (count == items.length) {
            //通知消费者消费,生产等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //否者丢入产品
        items[count] = item;
        count++;
        //通知消费者消费
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized Item pop() {
        //如果容器满了,就需要等待消费者消费
        if (count == 0) {
            //等待生产者生产,消费者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //否者可以消费
        count--;
        Item item = items[count];
        //吃完了,通知生产者生产
        this.notifyAll();
        return item;
    }
}

信号灯法

//信号灯法,标志位解决
public class TestPC2 {
    public static void main(String[] args) {
        Tv tv = new Tv();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

//生产者-->演员
class Player extends Thread{
    Tv tv;
    public Player(Tv tv){
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2==0){
                this.tv.play("快乐大本营播放中");
            }else {
                this.tv.play("抖音:记录美好生活!");
            }
        }
    }
}
//消费者-->观众
class Watcher extends  Thread{
    Tv tv;
    public Watcher(Tv tv){
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}
//产品-->节目
class Tv{
    //演员表演,观众等待 T
    //观众观看,演员等待 F
    String voice;//表演节目
    boolean flag = true;
    //表演
    public synchronized void play(String voice){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了: " + voice);
        //通知观众观看
        this.notifyAll();
        this.voice = voice;
        this.flag = !this.flag;
    }
    //观看
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了: " + voice);
        //通知演员表演
        this.notifyAll();
        this.flag = !this.flag;

    }
}

线程池

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//测试线程池
public class TestPool {
    public static void main(String[] args) {
        //1.创建服务,创建线程池
        //newFixedThreadPool 参数为:线程池大小
        ExecutorService service = Executors.newFixedThreadPool(10);
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //2.关闭连接
        service.shutdown();
    }
}

class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
posted @ 2020-05-03 18:14  YeCaiYu  阅读(163)  评论(0)    收藏  举报