多线程学习笔记20210121

多线程学习笔记

线程简介 为什么要有线程

用一只手做事情显然是效率比较低的 ,两只手,再来一个人 合作去完成某一件事情显然就会效率高了。

就是在程序执行中 出现了 供需不平衡

为了使程序更快,更高,更强,所以需要多线程。多个线程操作一个资源的时候,会出现混乱,就需要让线程对资源的操作有序,就需要用到锁,同步。

程序 线程 进程

在操作系统中运行的一些程序,这些程序就是进程, 一个进程可以有多个线程,比如 QQ,微信。可以和多个人一起聊天,B站看视频,又可以播放视频,同时发弹幕,就是多个线程

线程实现方法

  1. 继承 Thread类

    public class TestThread extends  Thread{
    
        //重写run方法
        @Override
        public void run() {
           //方法体、
            for (int i = 0; i <20 ; i++) {
                System.out.println("我是run方法" + i);
            }
        }
        public static void main(String[] args) {
    
            //创建线程对象  调用start
            TestThread testThread = new TestThread();
            testThread.start();
            //主线程
            for (int i = 0; i <20 ; i++) {
                System.out.println("我是main方法" + i);
            }
        }
    }
    // 执行结果如下
    我是main方法6
    我是main方法7
    我是run方法0
    我是run方法1
    我是main方法8
    我是main方法9
    我是main方法10
    我是run方法2
    
      //  多条执行路径,交替执行  cpu 调度线程
    
  2. 实现Runable接口

public class TestThread  implements   Runnable{

    //重写run方法
    @Override
    public void run() {
       //方法体、
        for (int i = 0; i <20 ; i++) {
            System.out.println("我是run方法" + i);
        }
    }
    public static void main(String[] args) {

        //创建线程对象  调用start
        TestThread testThread = new TestThread();
//        testThread.run();  这样调用也可以执行方法,本质上是顺序执行。

        new Thread(testThread).start();

    }
}


//多线程就是分时利用CPU,宏观上让所有线程一起执行 ,也叫并发
// 发现问题  实现接口之后 直接创建对象调用run方法也可以执行 是为什么

//结果: 可以运行  但是run() 方法调用还是顺序执行, 调用start() 是线程进入jvm虚拟机调度。这才是多线程执行真正的目的。
推荐实现接口 Runable 因为继承只能单继承,局限性。
  1. 实现Callable接口(可以有返回值)
package hy.test;

import java.util.concurrent.*;

//买票方法
public class TestCallable  implements Callable {

    //重写call方法有返回值
    @Override
    public Boolean call() {
        System.out.println(Thread.currentThread().getName() + "执行了call方法");
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        实现callable 接口 调用线程的步骤
        TestCallable t1 = new TestCallable();
        TestCallable t2 = new TestCallable();
        //1 创建执行服务 创建线程池
        ExecutorService service = Executors.newFixedThreadPool(2);
        //2 提交结果
        Future r1 = service.submit(t1);
        Future r2 = service.submit(t1);
        //3 获取结果
        boolean b1 = (boolean) r1.get();
        boolean b2 = (boolean) r2.get();
        System.out.println("b1 = " + b1);
        System.out.println("b2 = " + b2);
        //4 关闭服务
        service.shutdown();

    }


}

// 结论 使用callable 接口需要将线程对象提交到线程池中执行,call方法可以有返回值,可以有抛出异常。


  1. 线程池 Executors工具类可以创建三种线程

并发问题

多个线程(对象) 对同一个资源进行操作、

线程的状态

  • 创建状态
    • 线程一旦创建
  • 就绪状态
    • 调用start() 进入就绪状态
  • 运行状态
    • 这个状态就是执行run()方法中的代码
  • 阻塞状态
    • 当调用 sleep() , wait() , yield() 线程就会进入阻塞状态,阻塞状态解除之后,线程会重新进入就绪状态等待CPU调度
  • 死亡状态
    • 线程中断或者结束,一旦进入死亡状态,就不能再次启动。
方法说明
setPriority()更改线程的优先级
static void sleep (long millis)在设置的时间内让该线程休眠
void join()等待该线程终止
static void yield()暂停当前线程,执行其他线程(礼让,不一定会成功)
void interrupt()中断线程 不推荐使用
boolean inAlive()测试线程是否处于活动状态

让线程停止

推荐使用一个标志位来控制。

// 使用标志位 停止线程
public class TestThread  implements Runnable {
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while(flag){
            System.out.println("run线程执行中" + i++);
        }
    }
    public void  stop(){
        this.flag = false;
    }
    public static void main(String[] args) {
        TestCallable t = new TestCallable();
        new Thread(t).start();

        for (int i = 0; i < 100; i++) {

            if(i == 90){
                t.stop();
                System.out.println("run线程停止了");
            }
            System.out.println("main线程执行" + i);

        }
    }
}

线程休眠 sleep

休眠不会释放锁

守护线程(daemon)

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用邓艾守护线程执行完毕
  • 如 后台记录操作日志,监控内存,垃圾回收等待。。。
  • 守护线程结束就是程序运行结束之后
Tread thread = new Thread(xxx)
thread.setDaemon(true)

线程同步

同一个资源被多个线程操作—并发。

线程同步就是排队。

(synchronized): 拿到锁才可以操作资源。

每个线程都有自己的工作内存,所以会有数据不同步的线程,数据不可见。

Synchonized 关键字 锁对象或者锁模板类Class

这是个关键字,两种用法 synchronized方法 和 synchronized 代码块。 影响性能,,,

  • 用法1 关键字synchronized

//买票的安全问解决
public class TestTickets implements Runnable {
    private boolean flag = true;
    private int tickets = 10;
    @Override
    public void run() {
    while(flag){
        try {
            buy();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
   	 }
    }
    //同步方法 锁 this
    public  synchronized void  buy() throws InterruptedException {
        //买票
        if(tickets<=0){
            flag = false;
            return;
        }
       Thread.sleep(100);
        System.out.println(Thread.currentThread().getName()+"拿到票"+ tickets--);
    }
    public static void main(String[] args) {
        TestTickets t1 = new TestTickets();
        new Thread(t1,"小明").start();
        new Thread(t1,"小红").start();
        new Thread(t1,"张三").start();
    }
}




用法二 同步块

synchronized(obj){
    //代码块
    
    //obj 是同步监视器  可以是任何对象,但是推荐使用共享资源作为同步监视器
    //同步方法中无需指定同步监视器,因为同步方法的同步监视器就是把this,就是这个方法本身,或者Class
}

死锁

两个线程持有对方想获取的锁,都拿不到锁,僵持了、、、

//买票方法
public class MakeUp extends Thread {

    int choice;
    String name;
    static Mirror mirror = new Mirror(); //static 保证只有1份资源
    static Lipstick lipstick = new Lipstick();
    MakeUp(int choice,String name){
        this.choice = choice;
        this.name = name;
    }
    @Override
    public void run() {
        try {
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public  synchronized void  makeUp() throws InterruptedException {
        if(choice == 0){
            synchronized (lipstick){
                System.out.println(this.name + "获得口红锁");
                Thread.sleep(1000);
                synchronized(mirror){
                    System.out.println(this.name + "获得镜子锁");
                }
            }
        }else{
            synchronized (mirror){
                System.out.println(this.name + "获得口红锁");
                Thread.sleep(2000);
                synchronized(lipstick){
                    System.out.println(this.name + "获得镜子锁");

                }
            }
        }
    }
    public static void main(String[] args) {
        MakeUp t1 = new MakeUp(0,"小红");
        MakeUp t2 = new MakeUp(1,"小张");
        t1.start();
        t2.start();

    }
}
//镜子
class Mirror{

}

//口红
class Lipstick{

}
  • 互斥 条件 一个资源一次只能被一个进程使用’
  • 请求与保持条件: 一个人手里拿着西瓜排队打饭,队伍太长,等太久,手里的西瓜别人想要而他不放手
  • 不剥夺条件: 一个人正在上厕所,还没上完,其他人只能等着
  • 循环等待条件: A要B的东西 B要A的东西 僵持了

Lock JDK5.0+

更细颗粒度的锁

ReentrantLock 可重入锁

 Lock l = ...;
l.lock(); 
try { 
    // access the resource protected by this lock 
} finally {
    l.unlock();
} 
  • 怎么使用
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest implements Runnable{
    private int tickets = 10;
    //定义可重入锁
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){
            try {
                lock.lock(); //加锁
                if(tickets <=0){
                    break;
                }
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() +"=====" + tickets--);
            } finally {
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) {
        LockTest lockTest = new LockTest();
        new Thread(lockTest).start();
        new Thread(lockTest).start();
        new Thread(lockTest).start();
    }
}

线程通信

线程之间进行交流。生产者消费者模式。 管程法

package hy.test;

//生产者消费者模型  生产者  消费者  缓冲区 产品
public class  LockTest{
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();
        Produce produce = new Produce(synContainer);
        Consumer consumer = new Consumer(synContainer);

        produce.start();
        consumer.start();
    }
}


//生产者
class Produce extends  Thread{

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

   //生产者生产产品
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Product(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++) {
            container.pop();
            System.out.println("消费了"+ i + "个产品");
        }
    }
}

//产品
class Product{
    private int id;

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

}
//缓冲区
class SynContainer{

    //一个容器 存放产品
    Product[] arr = new Product[10];
    int count = 0;

    //生产者存放产品
    public synchronized void push(Product product){
        //如果容器满了就等待消费者取走产品
        if(count == arr.length){
            //通知消费者消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果容器没有满,就存放进去
        arr[count] = product;
        count++;
        //可以通知消费者消费了
        this.notifyAll();
    }
    //消费者获取产品

    public synchronized Product pop(){
      //判断是否有产品 如果没有等待生产
        if(count == 0 ){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //消费者消费。。
        //如果有产品就消费
        count--;
        Product product = arr[count];
        this.notifyAll();
        return product;
    }
}

线程池

使用Executors工具类创建 不推荐

package hy.test;

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

public class TestPool {

    public static void main(String[] args) {
        //创建一个指定数量的线程池
//        ExecutorService pool = Executors.newFixedThreadPool(3);
        //创建一个线程池
//        ExecutorService pool = Executors.newSingleThreadExecutor();
        //创建可变线程池
        ExecutorService pool = Executors.newCachedThreadPool();

//        pool.execute(new MyThread());
//        pool.execute(new MyThread());
//        pool.execute(new MyThread());

        pool.submit(new MyThread());
        pool.submit(new MyThread());
        pool.submit(new MyThread());
        pool.submit(new MyThread());
        //关闭连接
        pool.shutdown();
    }




}

class MyThread implements  Runnable{
    @Override
    public void run() {
      System.out.println(Thread.currentThread().getName() +"====== ");
    }
}


使用 ThreadPoolExecutor 自定义创建线程池

ThreadPoolExecutor(int corePoolSize,  //核心线程数 就是日常备战的
                   int maximumPoolSize, // 设置最大线程数量 
                   long keepAliveTime,  //设置活跃时长 一定时间之后大于核心线程数的线程会去休息
                   TimeUnit unit, //设置时间单位
                   BlockingQueue<Runnable> workQueue, //设置阻塞队列。可执行任务数量 就是能有多少人在那排队等待线程执行
 // 这个队列将仅保存execute方法提交的Runnable任务
                   ThreadFactory threadFactory, //线程创建工厂
                   RejectedExecutionHandler handler) //拒绝策略,就是线程池满了之后无法接受新的任务
创建一个新 ThreadPoolExecutor给定的初始参数。 

JUC 并发包

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值