Java多线程学习笔记

1.线程与进程

进程:
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。

线程:
是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程。
线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程。

1.线程调度

分时调度

  • 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。

抢占式调度

  • 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
  • CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核芯而言,某个时刻,只能执行一个线程,而CPU在多个线程之间切换速度相对我们的感觉要块,看上去技术在同一时刻运行。其实多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

2.同步与并发&并发与并行

  • 1.同步与异步 同步:排队执行,效率低但是安全。 异步:同时执行,效率高但是数据不安全。
  • 2.并发与并行 并发:指两个或多个事件在同一个时间段内发生。 并行:指两个或多个事件在同一时刻发生(同时发生)。

3.继承Thread

继承一个Thread就是一个新的线程。

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

public class MyRunnable extends Thread{
    public void run(){
    //这里的代码 就是一条新的执行路径,
    //这个执行路径的出发方式,不是调用run方法,
    //而是通过thread对象的start()来启动任务
        for(int i=0;i<10;i++){
            sout("床前明月光")
        }
    }
}

1.每个线程都拥有自己的栈空间,共用一份堆内存。
由一个线程执行的方法,这个方法也会执行在这个线程里面。

4.实现Runnable

//用于给线程进行执行的任务
public class MyRunnable implements Runnable{
    public void run(){
        //线程的任务
        for(int i=0;i<10;i++){
            sout("床前明月光")
        }
    }
}


//实现Runnable 与继承Thread相比又如下优势:
//1.通过创建任务,然后给线程分配的方式来实现的多线程。
//2.可以避免继承所带来的局限性。
//3.任务与线程本身是分离的,提高了程序的健壮性。
//4.后续学习的线程池技术,接收Runnable类型的任务,不接受Thread类型的任务。

public static void main(String[] args){
    //实现Runnable
    //1.创建一个任务对象
    MyRunnable r = new MyRunnable();
    //2.创建一个线程,并为其分配一个任务
    Thread t = new Thread(r);
    //3.执行这个线程
    t.start();
    for(int i=0;i<0;i++){
        sout("疑是地上霜")
    }
}

5.线程休眠sleep

public static void main(String[] args){
    //线程的休眠 sleep,每隔一秒打印
    for(int i=0;i<10;i++){
        sout(i);
        Thread.sleep(1000);
    }
}

6.设置和获取线程名称

public static void main(String[] args){
    //如何获取线程的名称
    sout(Thread.currentThread().getName());
    new Thread(new MyRunnable(),"锄禾日当午").start();//线程的名称是锄禾日当午
}

    static class MuRunnable implements Runnable{
        public void run(){
            sout(Thread.currentThread().getName());
        }
    }

7.线程的中断

//线程的中断
//一个线程是一个独立的执行路径,它是否应该结束,应该由其自身决定。
Thread t1 = new Thread(new MyRunnable());
//给线程t1添加中断标记
t1.interrupt();
static class MuRunnable implements Runnable{
        public void run(){
            sout(Thread.currentThread().getName());
            try{
            Thread.sleep(1000);
        }catch(InterruptedException e){
            sout("发现中断标记")//如果要让线程中断就return
            return;
        }
    }

8.守护线程

//线程:分为守护线程和用户线程
//用户线程:当一个进程不包含任何的存活的用户线程时,进行结束。
//守护线程:守护用户线程,当最后一个用户线程结束时,所有守护线程自动死亡。
Thread t1 = new Thread(new MyRunnable());
//设置t1为守护线程
t1.setDaemon(true);

9.线程安全问题

//线程不安全
//解决方案1. 同步代码块
//格式: synchronized(锁对象){}
psvm{
    Runnable run = new Ticket();
    new Thread(run).start();
    new Thread(run).start();
    new Thread(run).start();
}

//解决方案1. 同步代码块 隐式锁
//格式: synchronized(锁对象){}
static class Ticket implements Runnable{
        //只会创建一次,3个线程同用一个锁对象。
        private Object o = new Object();
        //票数
        private int count = 10;
        public void run(){
            while(true){
                //三个线程看同一个锁对象o,排队执行。
                synchronized(o){
                    if(count>0){
                        //卖票
                    sout("正在准备卖票")try{
                        Thread.sleep(1000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    count--;
                    sout("出票成功,余票"+count)
                    }
                }
             }
    }
    
    
    //解决方案2:同步方法 隐式锁
    //给方法增加synchronized修饰
    static class Ticket implements Runnable{
        //只会创建一次,3个线程同用一个锁对象。
        private Object o = new Object();
        //票数
        private int count = 10;
        public void run(){
            while(true){
                boolean flag = sale();
                if(!flag){
                    break
                }
    }
    
    //给方法增加synchronized修饰
    public synchronized boolean sale(){
                    if(count>0){
                        //卖票
                    sout("正在准备卖票")try{
                        Thread.sleep(1000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    count--;
                    sout("出票成功,余票"+count)
                    return true;
                    }
                }else{
                    return false;
                }
             }  
             
          
     //解决方案3显示锁 l
     //格式: lock l = new ReentrantLock();   
     static class Ticket implements Runnable{
        
        //票数
        private int count = 10;
        //显示锁
        lock l = new ReentrantLock();
        public void run(){
            while(true){
                    l.lock();
                    if(count>0){
                        //卖票
                    sout("正在准备卖票")try{
                        Thread.sleep(1000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    count--;
                    sout("出票成功,余票"+count)
                    }else{
                        break;
                    }
                    l.unlock();
                }
    }  

10.公平锁与非公平锁

  • 一起抢锁的是不公平锁,排队的是公平锁。
  • 显示锁,同步代码块,同步方法都是不公平锁。
  • 显示锁 l :fair参数为true 就表示公平锁

11.多线程通信问题

//多线程通信问题,生产者与消费者问题
public static void main(String[] args){
    Food f = new Food();
    new Cook(f).start();
    new Waiter(f).start();
}


//厨师
static class Cook extends Thread{
    private Food f;
    public Cook(Food f){
        this.f = f;
    }
     public void run(){
        for(int i=0;i<100;i++){
            //生成50份小米粥和50份煎饼果子
            if(i%2==0){
                f.setNameAndTaste("老干妈小米粥","香辣味");
            }else{
                f.setNameAndTaste("煎饼果子","甜辣味");
            }
        }
    }
}

//服务生
static class Waiter extends Thread{
    private Food f;
    public Waiter(Food f){
        this.f = f
    }
    public void run(){
        for(int i=0;i<100;i++){
            try{
                Thread.sleep(100);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            f.get();
        }
    }
}

//食物
static class food{
    private String name;
    private String taste;
    //标记是厨师做饭还是服务员端菜
    private boolean flag = true;
    public synchronized void setNameAndTaste(String name,String taste){
        if(flag){
            this.name = name;
            try{
                Thread.sleep(100);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            this.taste = taste;
            flag = false;
            //唤醒在当前this下的所有线程
            this.notifyAll();
            //唤醒之后厨师线程睡着
            this.wait();
        }
    }
    public synchronized void get(){
        if(!flag){
            sout("服务员端走的菜的名称是:"+name+"味道:"+taste);
            flag = true;
            this.notifyAll();
            this.wait();
        }
    }
}

12.线程的六种状态

  • new: 线程刚被创建,尚未启动的线程处于此状态。
  • Runnable(运行状态): Java虚拟机中执行的线程处于此状态。
  • Blocked(在排队时的状态): 被阻塞等待监视器锁定的线程处于此状态。
  • Waiting(在休眠的状态): 无期限等待另一个线程执行特定操作的线程处于此状态。
  • TIMED WAITING: 正在等待另一个线程执行最多指定等待时间操作的线程处于此状态。
  • TERMINATED: 已退出的线程处于此状态。

13.带返回值的线程Callable

1.//编写类实现Callable接口,实现call方法
class xxx implements Callable<T>{
    public <T> call() throws Exception{
        return T;
    }
}
2.//创建FutureTask对象,并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable);
3.//通过Thread,启动线程
new Thread(future).start();

14.线程池 Executors

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

1.线程池的好处

  • 降低资源消耗。
  • 提高响应速度。
  • 提高线程的可管理性。

2.线程池类型

//缓存线程池(长度无限制)
//任务加入后的执行流程:
    1.判断线程池是否存在空闲线程。
    2.存在则使用。
    3.不存在,则创建线程,并放入线程池,然后使用
public static void main(String[] args){
    //向线程池中加入新的任务
    ExecutorService service = Executors.newCachedThreadPool();
    service.execute(new Runnable(){
        public void run(){
            sout(Thread.currentThread().getName()+"锄禾日当午");
        }
    });
    service.execute(new Runnable(){
        public void run(){
            sout(Thread.currentThread().getName()+"锄禾日当午");
        }
    });
    service.execute(new Runnable(){
        public void run(){
            sout(Thread.currentThread().getName()+"锄禾日当午");
        }
    });
}


//定长线程池
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable(){
    public void run(){
        sout(Thread.currentThread().getName()+"锄禾日当午");
        try{
            Thread.sleep(3000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
});

service.execute(new Runnable(){
    public void run(){
        sout(Thread.currentThread().getName()+"锄禾日当午");
        try{
            Thread.sleep(3000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
});

service.execute(new Runnable(){
    public void run(){
        sout(Thread.currentThread().getName()+"锄禾日当午");
        try{
            Thread.sleep(3000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
});

//单线程线程池
//执行流程:
    1.判断线程池的那个线程是否空闲
    2.空闲则使用
    3.不空闲,则等待 池中的单个线程空闲后使用
public static void main(String[] args){
    //向线程池中加入新的任务
    ExecutorService service = Executors.newSingleThreadExector();
    service.execute(new Runnable(){
        public void run(){
            sout(Thread.currentThread().getName()+"锄禾日当午");
        }
    });
    service.execute(new Runnable(){
        public void run(){
            sout(Thread.currentThread().getName()+"锄禾日当午");
        }
    });
    service.execute(new Runnable(){
        public void run(){
            sout(Thread.currentThread().getName()+"锄禾日当午");
        }
    });
}

//周期任务 定长线程池
//执行流程
    1.判断线程池是否存在空闲线程
    2.存在则使用
    3.不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池,然后使用
    4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
    public static void main(String[] args){
        ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
        //1.定时执行一次
        //参数1.定时执行的任务
        //参数2.时长数字
        //参数3.时长数字的时间单位,TimeUnit的常量指定
        //下面代码5秒后打印我
        service.schdule(new Runnable(){
            public void run(){
                sout("我")
            }
        },5,TimeUnit.SECONDS);
    }
    
        //周期性执行任务
        //参数1.任务
        //参数2.延迟时长数字(第一次执行在上面时间以后)
        //参数3.周期时长数字(每隔多久执行一次)
        //参数4.时长数字的单位
        //下面的代码5秒后执行每次间隔一秒打印我
        service.scheduleAtFixedRate(new Runnable(){
            public void run(){
                sout("我");
            }
        },5,1,TimeUnit.SECONDS);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值