11.线程

线程

1. 专业术语

并行 vs 并发

并行:
  对于系统而言  执行多个任务 多个应用程序同时执行
并发:
  用户级别的并发
  高并发(并发量)--->分布式的项目--->浏览器缓存优化  服务器优化 tomcat  100个  mysql  151  
      服务器集群  负载均衡 
      
 你在吃饭  有人打电话   如果你是吃完饭之后  再去接电话  你既不支持并行又不支持并发
 你在吃饭  有人打电话   你吃了一口饭 说了一句话  你是支持并发的
 你在吃饭  有人打电话   你同时吃饭又说话   你是支持并行的

阻塞 vs 非阻塞

阻塞:
   程序等待。 程序没有终止   Scanner  状态
非阻塞:
   程序不会等待

同步 vs 异步

同步:
  一个任务的执行必须等待其他任务结束。  很大的可能会产生阻塞
异步:
 一个任务的执行不用等待其他任务的结束。 (异步请求  异步任务) 
   下订单  支付
    卡扣钱   信息通知   发邮件
     

进程 vs 线程

进程:
  与系统有关系 
   等同于一个应用程序   进程占据cpu与内存
      
 线程:
   隶属于进程。一个应用程序至少存在一个线程的。 线程与线程之间是相互独立的  没有关系的  共享进程资源,
   多线程就是属于并发  
同步阻塞:
   当前线程阻塞 处于等待的状态
       
同步非阻塞:
    当前线程正常运行
        
异步阻塞:
    (红灯)---> 1  2 多个线程等待
        
异步非阻塞:
    多个线程正常执行

2. 多线程

2.1 Thread

java语言支持多线程。 java.lang.Thread

public class Thread
extends Object
implements Runnable

每个线程都有优先级别 理论上 级别越高 优先被执行的概率就越高 实际中要看每个线程抢占cpu的时间效率

线程的执行依靠于CPU调度以及cpu上下文切换时间 1-10

static int MAX_PRIORITY   10
static int MIN_PRIORITY    1
static int NORM_PRIORITY  5

线程状态(线程的生命周期)

static class  Thread.State  

常用构造方法

Thread(Runnable target) 
Thread(Runnable target, String name) 
Thread(String name) 

常用功能方法

static Thread currentThread()  获得当前正在运行线程
long getId()  获得线程id
String getName()  /void setName(String name)  
int getPriority()  / void setPriority(int newPriority)  
Thread.State getState()  获得线程的状态
    
线程调度:    
void join() 等待当前线程死亡(优先让当前线程逻辑执行完毕) 
void join(long millis) /void join(long millis, int nanos)   优先让当前线程逻辑执行指定的时间

static void sleep(long millis)  / static void sleep(long millis, int nanos)  
 让当前线程休眠指定时间  超时了 自己醒过来  不会自动释放锁(监视器)对象
        
void start()  启动线程
    
static void yield() 让当前线程礼让其他线程 把抢占cpu机会让给其他的线程 自己又立马处于就绪的状态 
void interrupt() 中断当前线程

继承Object

void notify()  在另外一个线程里面随机唤醒一个处于wait的线程
void notifyAll()   在另外一个线程里面唤醒所有的wait的线程
    
void wait()  让当前线程一直处于等待状态  不会醒过来  自动释放锁对象  + synchornized
void wait(long timeout) / void wait(long timeout, int nanos) 

2.2 创建多线程

为什么要创建多线程? 多个任务 提高资源利用率的问题,

1. 继承Thread类

开启多个线程 下载小说资源
 public class NovelDownloadThread extends Thread {

    private String novelPath;
    private String targetPath;

    //重写父类的run
    @Override
    public void run() {
        NovelDownload.download(novelPath, targetPath);
        //线程调度
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public NovelDownloadThread(String name, String novelPath, String targetPath) {
        super(name);
        this.novelPath = novelPath;
        this.targetPath = targetPath;
    }
}   
public class DownLoadTest {

    public static void main(String[] args) {

        //开启多个线程
        String path1 = "https://read.qidian.com/chapter/YuM01v2tgLmdkflPPo43eA2/TF8tp5RUeaS2uJcMpdsVgA2";
        String path2 = "https://read.qidian.com/chapter/GtK3-_zw6Pvv7_WH3wfEdQ2/ZkY2ULRmHtP4p8iEw--PPw2";
        String path3 = "https://read.qidian.com/chapter/vUHUrXVfcSYKVgi5xLJPjA2/AEAX9qtyFphOBDFlr9quQA2";


        NovelDownloadThread thread1 = new NovelDownloadThread("a线程", path1, "day18/novel/a.txt");
        NovelDownloadThread thread2 = new NovelDownloadThread("b线程", path2, "day18/novel/b.txt");
        NovelDownloadThread thread3 = new NovelDownloadThread("c线程", path3, "day18/novel/c.txt");

//        System.out.println(thread1.getState());//NEW 新建状态
        //启动线程
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

2. 实现Runnable接口

单根继承

卖票:
  火车站有多个窗口(线程)卖票: 100
     3 
public class SaleTicket {

    private Integer num = 30;

    public void sale() {
        while (num > 0) {
            if (num > 0) {
                System.out.println(Thread.currentThread().getName() + "卖了第" + (num--) + "票");
                try {
                    TimeUnit.MILLISECONDS.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
 new Thread(()->saleTicket.sale(),"窗口1").start();
        new Thread(()->saleTicket.sale(),"窗口2").start();
        new Thread(()->saleTicket.sale(),"窗口3").start();

3. 实现Callable接口

@FunctionalInterface
public interface Callable<V>
V call()  
public class FutureTask<V>
extends Object
implements RunnableFuture<V>
    
public interface RunnableFuture<V>
extends Runnable, Future<V>
    
FutureTask(Callable<V> callable) 
转账:
  多个(用户)线程给同一个用户转账:
     
public class BalanceAccount implements Callable<Integer> {

    private static Integer balance = 1000;

    private Integer money;//存/取的钱数

    private boolean flag;

    @Override
    public Integer call() throws Exception {

        //多读多写
        if (flag) {
            balance += money;
            System.out.println(Thread.currentThread().getName() + "转给张三" + money + "余额:" + balance);
        } else {
            balance -= money;
            System.out.println(Thread.currentThread().getName() + "取了张三" + money + "余额:" + balance);
        }
        return balance;
    }

    public BalanceAccount(Integer money, boolean flag) {
        this.money = money;
        this.flag = flag;
    }

    public Integer getBalance() {
        return balance;
    }
}    

开启多个线程

public static void main(String[] args) {

        BalanceAccount account1 = new BalanceAccount(200,true);
        BalanceAccount account2 = new BalanceAccount(300,true);

        //Thread: 形参 Runnable
        //整合Callable和Runnable
        //Callable+Future(维护线程运行完毕之后数据)--->JUC FutureTask<?>

        FutureTask<Integer> task1 = new FutureTask<>(account1);
        FutureTask<Integer> task2 = new FutureTask<>(account2);

        new Thread(task1,"李四").start();
        new Thread(task2,"王五").start();


        try {
            System.out.println(task1.get());
            System.out.println(task2.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


        BalanceAccount balanceAccount = new BalanceAccount(0,false);
        System.out.println(balanceAccount.getBalance());//1500

    }
 public static void main(String[] args) {

        BalanceAccount account1 = new BalanceAccount(200, true);
        BalanceAccount account2 = new BalanceAccount(300, true);

        //2个线程
        //利用jdk提供默认的线程池  Executors
        //自定义创建

//        ThreadPoolExecutor  创建线程池的执行器
//        Executors.newCachedThreadPool() 缓存 池子的线程量可自动伸缩
//        Executors.newFixedThreadPool() 固定线程量
//        Executors.newScheduledThreadPool()
//        Executors.newSingleThreadExecutor()


        ExecutorService service = Executors.newCachedThreadPool();
        //需要将任务提交到线程池里面

        FutureTask<Integer> task1 = new FutureTask<>(account1);
        FutureTask<Integer> task2 = new FutureTask<>(account2);

        service.submit(task1);
        service.submit(task2);

        //获得每个任务执行功能之后  返回的数据
        try {
            System.out.println(task1.get());
            System.out.println(task2.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

//        service.shutdown();

        //自定义线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 100, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5));
        executor.submit(task1);
        executor.submit(task2);

        //多线程:
        //数据量  数据分析
        //异步任务   实时执行
    }
public class ThreadPoolExecutor extends AbstractExecutorService
    
corePoolSize:指定了线程池中的线程数量,它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去;

maximumPoolSize:指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量;

keepAliveTime:当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁;

unit:keepAliveTime的单位

workQueue:任务队列,被添加到线程池中,但尚未被执行的任务;
    它一般分为直接提交队列SynchronousQueue、有界任务队列ArrayBlockingQueue、无界任务队列LinkedBlockingQueue、优先任务队列PriorityBlockingQueue几种;

threadFactory:线程工厂,用于创建线程,一般用默认即可;

handler:拒绝策略;当任务太多来不及处理时,如何拒绝任务;
public class Money {


    private Integer balance = 1000;

    public void saveMoney(int money) {
        balance += money;
        System.out.println(Thread.currentThread().getName() + "给张三转账" + money + "余额:" + balance);
    }


    public void drawMoney(int money) {
        balance -= money;
        System.out.println(Thread.currentThread().getName() + "取了张三" + money + "余额:" + balance);
    }

    public Integer getBalance() {
        return balance;
    }
}

public static void main(String[] args) {

        Money money = new Money();

       /* //创建多线程
        List<Thread> list = new ArrayList<>();

        list.add(new Thread(()->money.saveMoney(500),"李四"));
        list.add(new Thread(()->money.drawMoney(800),"张三wife"));

        list.forEach(Thread::start);
        //等待前2个线程死亡  join
        try {
            for (Thread thread : list) {
                thread.join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(money.getBalance());*/
        FutureTask<Integer> task1 = new FutureTask<>(()->{
            money.saveMoney(500);
            return money.getBalance();
        });

        FutureTask<Integer> task2 = new FutureTask<>(()->{
            money.drawMoney(800);
            return money.getBalance();
        });

       new Thread(task1,"李四").start();
       new Thread(task2,"张三wife").start();

        try {
            System.out.println(task1.get());
            System.out.println(task2.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

2.3 线程安全

在多线程的环境下 多个线程有共享的资源 有可能会出现线程安全的问题

计算机内存模型:
   Memory Model
   Cpu 调用指令  读写内存数据
       
   cpu执行效率   内存读写数据效率  等同
   CPU一值升级  处理效率越来越快    内存读写数据效率一般
   缓存:  cpu  缓存(内存数据副本)   内存      
  
       单核:  1L 2L
       多核: 每个核里面都有1L 2L
           
 就是计算机里面缓存一致性的问题(线程安全)
 处理器优化/指令重排    cpu效率
           // 初始化  new  2
           // 开辟空间  3
           // 赋值  1
           
 在jvm里面  jmm----> 并发编程---> 可见性   原子性   有序性
   可见性: 一个线程的数据改变  对其它线程也是可见的  就是解决内存数据不一致的问题的  synchornized  volatile  Lock
   原子性: 一个整体  cpu执行功能的时候  调度  要么全部执行  要么全部不执行 synchornized   Lock
   有序性: 限制计算机指令重排  volatile   (synchornized   Lock保证程序有序性)
       
       
       synchornized/Lock 可以解决  可见性   原子性  有序性 (多读多写)
       volatile: 可见性  有序性  不能保证原子性  限制计算机指令重排  (修饰属性) (一写多读)                       

1. synchornized

自动上锁 自动释放锁

同步代码块(优先)

synchornized(同一个监视器对象/锁对象){//任意一个对象都可能监视器对象  对象锁
    //有可能会出现线程安全的逻辑
}
public void sale() {
        while (num > 0) {
            synchronized ("锁") {//上锁
                System.out.println(Thread.currentThread().getName() + "卖了第" + (num--) + "票");
            }//释放锁
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
public class Money {

    private static Integer balance = 1000;

    public void saveMoney(int money) {
       synchronized ("a") {
           balance += money;
           System.out.println(Thread.currentThread().getName() + "给张三转账" + money + "余额:" + balance);
       }
    }


    public void drawMoney(int money) {
        synchronized ("a") {
            balance -= money;
            System.out.println(Thread.currentThread().getName() + "取了张三" + money + "余额:" + balance);
        }
    }

    public Integer getBalance() {
        return balance;
    }
}

同步方法

public synchronized abc(){
    //有可能会出现线程安全逻辑
}
 public void sale() {
        while (num > 0) {
            a();
        }
    }
    
    private synchronized void a() {//上锁   锁对象?  多个线程同一个锁对象  当前类对象 this  对象锁
        if (num > 0) {
            System.out.println(Thread.currentThread().getName() + "卖了第" + (num--) + "票");
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }//释放锁
public static synchronized void saveMoney(int money) {
        //普通方法: 当前类对象  锁对象  this  对象锁
        //static方法  当前类的class  Money.class  类锁
        balance += money;
        System.out.println(Thread.currentThread().getName() + "给张三转账" + money + "余额:" + balance);
    }


    public static synchronized void drawMoney(int money) {
        balance -= money;
        System.out.println(Thread.currentThread().getName() + "取了张三" + money + "余额:" + balance);
    }

2. Lock

功能类似synchornized 手动上锁 手动解锁 ReentrantLock

 private static final Lock lock = new ReentrantLock();
    public void sale() {
        while (num > 0) {

            lock.lock();//上锁

            try {
                if (num > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖了第" + (num--) + "票");
                    try {
                        TimeUnit.MILLISECONDS.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            } finally {
                lock.unlock();//解锁
            }
        }

    }

2.4 死锁

多线程环境下 有2个共享资源 有2个锁对象

张三: 画笔   
李四: 颜料
    public static void main(String[] args) {

        //创建2个线程
        String pen = "画笔";
        String color = "颜料";

        //交叉锁+嵌套锁
        new Thread(() -> {
            synchronized (pen) {//上锁
                System.out.println(Thread.currentThread().getName() + "有" + pen);
            }//解锁
            synchronized (color) {
                System.out.println(Thread.currentThread().getName() + "拿到李四的" + color);
            }

        }, "张三").start();

        new Thread(() -> {
            synchronized (color) {//上锁
                System.out.println(Thread.currentThread().getName() + "有" + color);
            }//解锁
            synchronized (pen) {
                System.out.println(Thread.currentThread().getName() + "拿到张三的" + pen);
            }
        }, "李四").start();

        //阻塞
        //进行良好通信 实现资源共享
        //锁对象正常释放
    }

2.5 线程通信

体现在 生产者 与 消费者问题 (消息队列的原理)

生产者:
   产生的数据  都放在了缓冲区     
消费者:
   从缓冲区里面 获得数据   消费数据
       
双十一
  用户下订单---> 提交给支付宝(订单池) ---> 支付功能
public class UserThread extends Thread {

//    private OrderPool orderPool;//null

    @Override
    public void run() {
        //一直提交订单
        while (true) {
//            orderPool.addOrder();
            OrderPool.addOrder();
        }
    }

//    public UserThread(String name, OrderPool orderPool) {
//        super(name);
//        this.orderPool = orderPool;
//    }


    public UserThread(String name) {
        super(name);
    }
}
public class AiPayThread extends Thread {

//    private OrderPool orderPool;//null

    @Override
    public void run() {
        //一直处理订单
        while (true) {
            OrderPool.dealOrder();
        }
    }

//    public AiPayThread(String name, OrderPool orderPool) {
//        super(name);
//        this.orderPool = orderPool;
//    }


    public AiPayThread(String name) {
        super(name);
    }
}
public class OrderPool {


    //容器: 集合
    private static List<String> pool = new CopyOnWriteArrayList<>();//30个订单
    private static final Integer MAX_COUNT = 10;

    //用户提交订单
    public static void addOrder() {
        synchronized (OrderPool.class) {
            try {
                if (pool.size() == MAX_COUNT) {
                    OrderPool.class.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            pool.add("order");
            System.out.println(Thread.currentThread().getName() + "提交了一个订单,目前pool里面有:" + pool.size());

            //在生产者线程里面  唤醒消费者消费订单
            OrderPool.class.notifyAll();

            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //满足于支付宝处理订单
    public static void dealOrder() {//锁对象  this
        synchronized (OrderPool.class) {
            try {
                if (pool.size() == 0) {
                    //消费者线程等待着生产者生产数据
                    OrderPool.class.wait();//当前线程一直等待  阻塞状态
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (pool.size() > 0) {
                pool.remove(0);//删除第一个元素
                System.out.println(Thread.currentThread().getName() + "处理一个订单,目前pool里面有:" + pool.size());
                //在消费者线程里面唤醒生产者生产订单
                OrderPool.class.notifyAll();
                try {
                    TimeUnit.MILLISECONDS.sleep(900);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

2.6 线程生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W8DogyUE-1608513369230)(pic\image-20201219170548800.png)]

public enum State {  
        NEW,
        RUNNABLE,//run方法

        /**
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * <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>
         * 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;
    }

2.7. 停止线程

class StopThread extends Thread {

    private boolean flag = true;

    private int count = 1;

    @Override
    public void run() {

        while (flag) {
            System.out.println(Thread.currentThread().getName() + "===" + (count++));
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

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

upport#parkNanos LockSupport.parkNanos}
*

  • {@link LockSupport#parkUntil LockSupport.parkUntil}

  • *
    */
    TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }
    
    
    
    
    ## 2.7. 停止线程
    
    ```java
    class StopThread extends Thread {
    
        private boolean flag = true;
    
        private int count = 1;
    
        @Override
        public void run() {
    
            while (flag) {
                System.out.println(Thread.currentThread().getName() + "===" + (count++));
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
        }
    
        public void stopFlag() {
            this.flag = false;
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值