多线程基础学习

目录

  • 线程简介
  • 线程实现 (重点)
  • 线程状态
  • 线程同步 (重点)
  • 线程通信问题
  • 高级主题

观看JDK官方文档: Java 8 中文版 - 在线API中文手册 - 码工具

一、进程与线程

2.线程创建

2.1 继承Thread

线程不一定立即执行,CPU安排调度

run: 线程的执行体

2.2 实现Runnable

2.3 线程不安全,买票重复

package com.spring.base.thread;

/**
 * 不安全的买票, 线程不安全,有负数
 */
public class UnsafeBuyTicket {

    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"小黑").start();
        new Thread(buyTicket,"小白").start();
        new Thread(buyTicket,"小兰").start();
    }

}

class BuyTicket implements Runnable{

    private int ticketNums = 10;
    
    private boolean flag = true;

    @Override
    public void run() {
        while(flag){
            try {
                buy();
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
    }

    // 买票
    private  void buy() throws InterruptedException {
        if (ticketNums <= 0){
            flag = false;
            return;
        }
        //模拟延时
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName()+"买到了第"+ticketNums+"票");
        ticketNums--;
    }

}

2.4 龟兔赛跑

package com.spring.base.thread;

/**
 * 龟兔比赛
 */
public class Race {

    public static void main(String[] args) {
        RaceRunnable raceRunnable = new RaceRunnable();

        new Thread(raceRunnable,"兔子").start();
        new Thread(raceRunnable,"乌龟").start();

    }

}

/**
 * 模拟龟兔赛跑
 */
class RaceRunnable 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 = getWinner(i);

            //如果比赛结束了,就终止比赛
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
        }

    }

    //判断是否完成比赛
    public static Boolean getWinner(int i){
        if (winner !=null){
            return true;
        }else{
            if (i>=100) {
                winner = Thread.currentThread().getName();
                System.out.println(winner + "赢了");
                return true;
            }
        }
        return false;
    }

}

2.5 实现 Callable接口

3. 静态代理模式

4. Lamda 表达式

4.1 代码推导 Lambda 表达式

package com.spring.base.thread;

/**
 * 演示: 代码推导 Lambda 表达式
 */
public class FunctionalInterface {

    public static void main(String[] args) {

        ILike like = new ILikeImpl();
        like.lambda();

        like = new TestLambda.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 = ()-> {};
        like.lambda();


    }

}

/**
 * 1.定义一个函数式接口
 * 2.实现类
 * 3.静态内部类
 * 4.局部内部类
 * 5.匿名内部类
 * 6. 用lambda简化
 */

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


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

    @Override
    public void lambda() {
        System.out.println("i like lambda");
    }
}

//3.静态内部类
class TestLambda{
    static class Like2 implements ILike{

        @Override
        public void lambda() {
            System.out.println("i like lambda2");
        }

    }
}


interface Love{
    void lambda(int a, int b);
}

class test {
    public static void main(String[] args) {
        //1. lambda 表示简化  
        Love love = (int a,int b) ->{
            System.out.println(a+b);
        };

        //2. 简化2,简化括号
        love = (a,b) ->{
            System.out.println(a);
            System.out.println(b);
    };
};

总结:

  1. lambda 表达式 只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹.
  2. 前提是接口为函数式接口
  3. 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号

5.线程的状态

5.1 线程休眠

/**
 * 线程sleep
 */
public class ThreadSleepTest {

    public static void main(String[] args) {
        //打印当前时间
        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();
            }
        }
    }

}

5.2 线程礼让

/**
 * 线程礼让
 */
public class MyYieldTest {

    public static void main(String[] args) {
        Myyield myyield = new Myyield();
        new Thread(myyield,"小明").start();
        new Thread(myyield,"小黑").start();
    }

}


/**
 * 礼让线程
 */
class Myyield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }

}

5.3 Join合并线程

/**
 * 线程合并 join
 *
 */
public class TestJoin implements Runnable{

    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread t = new Thread(testJoin);
        t.start();

        for (int i = 0; i < 50; i++) {
            if (i==7){
                t.join();
            }
            System.out.println("main..."+i);
        }
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("join..."+i);
        }
    }

}

5.4 线程状态观测

 public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,
 
        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,
 
        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,
 
        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,
 
        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,
 
        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

public static enum Thread.State extends Enum<Thread.State>

线程状态。 线程可以处于以下状态之一:

  • NEW

尚未启动的线程处于此状态。

  • RUNNABLE

在Java虚拟机中执行的线程处于此状态。

  • BLOCKED

被阻塞等待监视器锁定的线程处于此状态。

  • WAITING

正在等待另一个线程执行特定动作的线程处于此状态。

  • TIMED_WAITING

正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。

  • TERMINATED

已退出的线程处于此状态。

一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。

/**
 * 观测线程的状态
 */
public class TestState {

    public static void main(String[] args) {
        Thread t = new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("///");
        });

        //观察状态
        Thread.State state = t.getState();
        System.out.println("观察状态: "+state);

        //观察启动后
        t.start(); // 启动线程
        state = t.getState();
        System.out.println("观察启动后: "+state); // RUN

        while(state != Thread.State.TERMINATED){ // 只要线程不终止,就一直输出状态
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            state = t.getState();  //更新线程状态
            System.out.println(state);  //输出状态
        }

        t.start();
        
    }

}

5.5 线程优先级

5.6 守护线程 (daemon)

  • 线程分为用户线程和守护线程,默认是用户线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如: 后台记录操作日志,监控内存,垃圾回收等等
public class DaemonThread {

    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread godThread = new Thread(new God());
        godThread.setDaemon(true); // 默认为false, 用户线程,正常的线程都是用户线程
        godThread.start(); // 守护线程启动

        new Thread(new You()).start();
        
    }

}

class God implements Runnable{

    @Override
    public void run() {
        while(true){
            System.out.println("上帝会一直保护着你");
        }
    }
}

class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("你一生都开心的活着");
        }
        System.out.println("============goodbye world==============");
    }
}

5.7 线程同步 - 多个线程操作同一个资源

6.线程不安全

6.1 三个案例

list添加,线程不安全

/**
 * 线程不安全的集合
 */
public class UnsafeList {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {

             new Thread(()->{
                 list.add(Thread.currentThread().getName());
             }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
    
}
public class UnsafeList {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
             new Thread(()->{
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
             }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

银行账户取钱

/**
 * 不安全取钱操作,两人去银行取钱
 */
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"基金");
        new Drawing(account,50,"小明").start();
        new Drawing(account,90,"小红").start();

    }
}

// 银行账户
class Account{
    int money;
    String name;

    public Account(int money,String name){
        this.money = money;
        this.name = name;
    }

}

// 银行: 模拟取款
class Drawing extends Thread{

    //账户
    Account account;
    //取了多少钱
    int drawingMoney;
    //现在手里有多少钱
    int nowMoney;

    public Drawing(Account account,int drawingMoney,String name){
        this.account = account;
        this.drawingMoney = drawingMoney;
        super.setName(name);
    }

    //取钱
    @Override
    public void run() {
        //判断有没有钱
        if (account.money - drawingMoney < 0){
            System.out.println(Thread.currentThread().getName() + "钱不够,取不了");
            return;
        }

        //延时任务: 发现问题,取出的总数为负数
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //卡内余额 = 余额 - 取的钱
        account.money = account.money - drawingMoney;
        //手里的钱
        nowMoney = nowMoney + drawingMoney;

        System.out.println(account.name+"余额为: "+account.money);
        System.out.println(this.getName()+"手里的钱: "+nowMoney);
    }

}

买票案例

/**
 * 不安全的买票, 线程不安全,有负数
 */
public class UnsafeBuyTicket {

    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"小黑").start();
        new Thread(buyTicket,"小白").start();
        new Thread(buyTicket,"小兰").start();
    }

}

class BuyTicket implements Runnable{

    private int ticketNums = 10;
    
    private boolean flag = true;

    @Override
    public void run() {
        while(flag){
            try {
                buy();
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
    }

    // 买票
    private synchronized void buy() throws InterruptedException {
        if (ticketNums <= 0){
            flag = false;
            return;
        }
        //模拟延时
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName()+"买到了第"+ticketNums+"票");
        ticketNums--;
    }

}

6.2 同步方法

锁的对象是变化的量

安全的集合CopyOnWrite,使用lock锁,保证线程安全

7.死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形.某一个同步块同时拥有"两个以上对象的锁"时,就可能会发生"死锁"问题.

7.1死锁避免方法

产生死锁的四个必要条件:

  1. 互斥条件: 一个资源每次只能被一个进程使用
  2. 请求与保持条件: 一个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺条件: 进程已获得的资源,在未使用完之前,不能强行剥夺
  4. 循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系

上面列出了死锁的四个必要条件,我们只要想办法破坏其中的任意一个或多个条件就可以避免死锁发生

package com.spring.base.thread;


/**
 * 死锁: 多个线程互相抱着对方需要的资源,然后形成僵持
 */
public class DeadLock {

    public static void main(String[] args) {
        Makeup makeup1 = new Makeup(0,"灰姑娘");
        Makeup makeup2 = new Makeup(1,"白雪公主");

        makeup1.start();
        makeup2.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(2000);
                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+"获得口红的锁");
                }
            }

        }

    }

}

7.2 解决死锁问题,不同时抱有两把锁

    //化妆,互相持有对方的锁,就是需要拿到对方的资源
    private void makeup() throws InterruptedException {
        if (choice==0){
            synchronized (lipstick){ // 获得口红的锁
                System.out.println(this.girlName+"获得口红的锁");
                Thread.sleep(2000);
            }
            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+"获得口红的锁");
            }
        }
    }

8. Lock(锁)

9. 线程协作

PC

9.1 管程法

生产者生产鸡,消费者吃鸡,通过公共资源缓冲区

package com.spring.base.thread;

/**
 * 管程法
 */
public class TestPc {

    public static void main(String[] args) {
        SyncContainer container = new SyncContainer();
        Productor productor = new Productor(container);
        productor.setName("生产者");
        productor.start();
        Consumer consumer = new Consumer(container);
        consumer.setName("消费者");
        consumer.start();
    }

}

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

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

}

//生产者
class Productor extends Thread{

    SyncContainer container;

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

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

}

//消费者
class Consumer extends Thread{

    SyncContainer container;

    public Consumer(SyncContainer syncContainer){
        this.container = syncContainer;
    }

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

//缓冲区
class SyncContainer{

    //需要一个容器大小
    Chicken[] chickens = new Chicken[10];

    //容器计数器
    int count = 0;

    //生产者放入产品
    public synchronized void push(Chicken chicken){
        //如果容器满了,就需要等待消费者消费
        if (count == chickens.length){
            //通知消费者消费,生产者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //如果容器没有满,我们就需要丢入产品
        chickens[count] =chicken;
        count++;

        //可以通知消费者消费了
        this.notifyAll();

    }

    //消费者消费产品
    public synchronized Chicken pop(){
        //判断消费者能否消费
        if (count == 0){
            //消费者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //如果可以消费
        count--;
        Chicken chicken = chickens[count];

        //吃完了,通知生产者生产
        this.notifyAll();
        return chicken;
    }
    
}

9.2 信号灯法

package com.spring.base.thread;

/**
 * 线程通信: 信号灯法, 通过flag进行灯的控制,为true灯时,等待表演,为false时,观看表演
 */
public class ActorThread {

    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 < 10; 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 < 10; i++) {
            this.tv.watch();
        }
    }

}

//产品 --> 节目
class TV{

    //演员表演,观众等待 T
    //观众观看,演员等待
    String voice;   // 表演的节目
    boolean flag = true;

    //表演
    public synchronized void play(String voice){
        //为false时,进入等待观众观看
        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(){
        //为true时,等待表演
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了"+voice);
        //通知表演演戏
        this.notifyAll();
        this.flag = !this.flag;
    }

}

10. 线程池

package com.spring.base.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 线程池
 */
public class ThreadSleepTest {

    public static void main(String[] args) {
        //创建服务,创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        //执行
        executorService.execute(new MyTestThread());
        executorService.execute(new MyTestThread());
        executorService.execute(new MyTestThread());
        executorService.execute(new MyTestThread());

        //关闭线程池
        executorService.shutdown();
    }

}

class MyTestThread implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值