java基础-多线程

多线程

程序:死的静态的功能的集合
进程:当程序运行起来就会开辟一个进程,多次运行程序会开辟多个进程
线程:一个进程内可以存在多条线程至少有一个线程
并行:多核CPU处理多个任务,多个任务同时进行
并发:单核cpu为例:貌似是一起执行,实际是在不停切换


如何编写多线程程序

继承Thread类

采用继承的方式创建多线程
    1.创建一个类继承Thread类
	2.重写run()方法
	3.启动线程:创建线程对象,使用线程对象调用start()方法来完成启动
Thread.currentThread().setName();设置当前线程名字
Thread.currentThread().getName();获取当前线程名字
public class Test {
    public static void main(String[] args) {
        //创建兔子线程
        RabbitThread rabbitThread = new RabbitThread();
        rabbitThread.setName("小兔子");//设置兔子线程名字
        rabbitThread.start();

        Thread.currentThread().setName("小乌龟");//设置当前线程名字

        while (true){
            System.out.println(Thread.currentThread().getName()+"跑");
        }
    }
}
class RabbitThread extends Thread{
    @Override
    public void run() {
        while (true){
            System.out.println(Thread.currentThread().getName()+"跑");
        }
    }
}

实现Runnable接口

实现的方式创建多线程
1.创建一个类实现Runnable接口
2.重写run()方法
3.创建Runnable对象
4.创建Thread类,将Runnable对象作为参数进行传递
5.调用start()方法启动线程

继承的方式与实现的方式做对比
1.继承的方式简单
2.实现的方式可以更好地实现资源共享
public class Test {
    public static void main(String[] args) {
        //创建Runnable对象
        RabbitRunnable rabbitThread = new RabbitRunnable();
        //创建Thread类,将Runnable对象作为参数进行传递
        Thread thread = new Thread(rabbitThread);
        thread.setName("小兔子");//设置兔子线程名字
        thread.start();//开启线程

        Thread.currentThread().setName("小乌龟");//设置当前线程名字

        while (true){
            System.out.println(Thread.currentThread().getName()+"跑");
        }
    }
}
class RabbitRunnable implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println(Thread.currentThread().getName()+"跑");
        }
    }
}

可以采用匿名内部类的方式实现多线程

//创建了匿名子类对象
Thread t1 = new Thread( ) {
	@Override
	public void run() {
		while (true) {
			System.out.println("乌龟跑");
		}
	}
};
t1.start();
new Thread(new Runnable() {//创建了一个Runnable的匿名子类对象
	@Override
	public void run() {
		while (true) {
			System.out.println("兔子跑");
		}
	}
}).start();

采用构造器的方式实现多线程

public class TestThreadConstructor {
    public static void main(String[] args) {

        /*RabbitThread r1 = new RabbitThread("灰兔子");
        RabbitThread r2 = new RabbitThread("白兔子");
        r1.start();
        r2.start();*/

        TortoiseRunnable tortoiseRunnable = new TortoiseRunnable();
        Thread thread = new Thread(tortoiseRunnable);
        thread.setName("小乌龟");
        thread.start();
        Thread thread1 = new Thread(tortoiseRunnable,"忍者神龟");
        thread1.start();
    }
}
class RabbitThread extends Thread{

    public RabbitThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        while (true){
            System.out.println(this.getName()+"跑");
        }
    }
}
class TortoiseRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {

            System.out.println(Thread.currentThread().getName()+"跑");
        }
    }
}

常用方法

1.isAlive():判断当前线程是否处在活跃状态
2.setPriority(优先级):1<=优先级<=10
	如果没有指定优先级,默认都是5
	优先级特别低也会有执行的机会
	先去执行优先级高的再去执行优先级低的
3.Thread.sleep(数字);:让当前线程睡眠一段时间(让当前线程进入阻塞状态)
	单位是ms
4.join():强行插队,插队的线程调用join()方法,被插队的线程要进行等待,等插队的线程执行完毕后才能继续执行
join(2000):强行插队,插队的线程调用join()方法,被插队的线程要进行等待2000ms,等插队的线程执行完毕后或者时间已到,才能继续执行
5.yield();:线程的礼让(特别不明显)
	将线程从运行状态转为就绪状态
6.stop():结束当前线程
7.setDaemon(true):守护线程
	哪一个线程调用了该方法就是守护线程
8.volatile:保证线程间的数据的可见性

实现Callable接口

线程池

线程安全

  1. 线程安全问题:当多个线程操作共享数据时,有可能会发生线程安全问题
  2. 解决线程安全问题
  3. 注意: 局部变量不能作为共享资源

同步代码块

语法结构
synchronized(同步监视对象){
]
同步监视对象:
1.必须是引用数据类型
2.当多条线程操作共享数据,同步监视对象,必须是同一个
3.同步代码块意义:保证在同步代码块内只要一条线程在执行,其他线程需要在同步代码块外等待
4.当一条线程进入同步代码块内,那么其他线程不能进入拥有同一个同步监视器对象的同步代码块

同步方法

同步方法的同步监视器是this
当有一条线程进入同步方法,那么其他线程不仅不能进入此方法,也不能进入拥有同一个人同步监视器对象的同步方法
public class Test {
    public static void main(String[] args) {
        WindowRunnable windowRunnable = new WindowRunnable();
        Thread t1 = new Thread(windowRunnable,"窗口一");
        Thread t2 = new Thread(windowRunnable,"窗口二");
        Thread t3 = new Thread(windowRunnable,"窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}
class WindowRunnable implements Runnable{

    int count = 60;

    @Override
    public void run() {

        while (true){
            if (count <= 0){
                break;
            }
            saleTicket();
        }
    }

    private synchronized void saleTicket() {
        if (count<=0){
            return;
        }
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"卖了第"+count+"张票");
        count--;
    }
}

Lock:公平锁和非公平锁

单例模式

一种代码模板
单例模式:包装产生的对象是唯一的,无论产生多少个对象,都是唯一的
单例模式分为两种:
1. 饿汉式:一开始创建类的时候,就把此对象创建出来
2. 懒汉式:等到需要的时候,再次创建出来
懒汉式可能出现线程安全问题

public class Test {
    public static void main(String[] args) {
        HungerSingle i1 = HungerSingle.INSTANCE;
        HungerSingle i2 = HungerSingle.INSTANCE;
        System.out.println(i2==i1);//true

        HungerSingle1 i3 = HungerSingle1.INSTANCE;
        HungerSingle1 i4 = HungerSingle1.INSTANCE;
        System.out.println(i3==i4);//true

        LazySingle i5 = LazySingle.getInstance();
        LazySingle i6 = LazySingle.getInstance();
        System.out.println(i5 == i6);//true
    }
}
class HungerSingle{
    public static final HungerSingle INSTANCE = new HungerSingle();
    private HungerSingle() {
    }
}
enum HungerSingle1{
    INSTANCE
}
class LazySingle{
    private static LazySingle lazySingle;
    private LazySingle(){

    }
    public static synchronized LazySingle getInstance(){
        if (lazySingle == null) {
            synchronized (Test.class){
                if (lazySingle == null) {
                    lazySingle = new LazySingle();
                }
            }
        }
        return lazySingle;
    }
}

等待唤醒机制

线程通信:

  1. wait():进行等待
  2. wait(时间):
  3. notify:通知;如果有多个线程等待,则随机通知一个线程
  4. notifyAll():通知所有等待的线程干活
线程通信使用同步代码块实现
/*
    一个吧台 一个厨师 一个服务员
    吧台最多放10份菜
    厨师已经做了10道菜则进行等待,通知服务员上菜
    服务员上菜,当服务员把所有的菜都上完了,服务员等待,通知厨师做菜

    问题1:厨师和服务员使用两个吧台
    解决:将吧台声明到Test类中

    问题2:厨师还没做菜,服务员已经开始上菜
    解决:保证线程安全,使用同步代码块

    问题3.厨师做的菜已经超出了吧台的能力,服务员端的菜是负数(无)
    解决:当吧台的菜已经<0时,服务员等待;当吧台的菜>0时,厨师等待
    
    wait()释放锁
*/
public class Test {
    public static void main(String[] args) {
        Bar bar = new Bar();
        CookThread cookThread = new CookThread(bar);
        cookThread.setName("厨师");
        WaiterThread waiterThread = new WaiterThread();
        waiterThread.setBar(bar);
        waiterThread.setName("服务员");
        cookThread.start();
        waiterThread.start();
    }
}
class  Bar{
    public static final int MAX_NUM = 10;//只能放10道菜
    int count;//记录吧台上菜的数量
}
class CookThread extends Thread{
    private Bar bar;
    public CookThread(Bar bar) {
        this.bar = bar;
    }
    @Override
    public void run() {
        while (true){
            //同步代码块 :保证线程安全
            synchronized (bar){//同步监视器使用共享资源
                //厨师在做菜之前对数据进行校验
                if (bar.count >= Bar.MAX_NUM) {//厨师做的菜已经>=吧台能够放菜的最大份数
                    try {
                        bar.wait();//厨师等待:采用同步监视器调用
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                bar.count++;//厨师做了一道菜
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(this.getName()+"做了一道菜,现在有"+bar.count+"份菜");
                bar.notify();//通知服务员端菜
            }
        }
    }
}
class WaiterThread extends Thread{
    private Bar bar;
    public void setBar(Bar bar) {
        this.bar = bar;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (bar) {
                //在端菜之前进行校验
                if (bar.count <= 0) {
                    try {
                        bar.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                bar.count--;//服务员端走了一份菜
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(this.getName() + "端走了一道菜,还有" + bar.count + "份菜");
                bar.notify();//通知厨师做菜
            }
        }
    }
}

了解死锁

/*
    产生死锁的原因:两条线程互相持有对方的锁资源,不放松
    可以让一条线程先跑完再让另一条线程在执行
* */
public class TestDeadLock {
    public static void main(String[] args) {
        Object goods = new Object();
        Object money = new Object();
        GoodsThread goodsThread = new GoodsThread(goods, money);
        CustomerThread customerThread = new CustomerThread(goods, money);
        goodsThread.start();
        try {
            Thread.sleep(1000);// 可以让一条线程先跑完再让另一条线程在执行
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        customerThread.start();
    }
}
class GoodsThread extends Thread{
    Object goods;
    Object money;

    public GoodsThread(Object goods, Object money) {
        this.goods = goods;
        this.money = money;
    }

    @Override
    public void run() {
        synchronized (money) {
            System.out.println("先给钱");
            synchronized (goods){
                System.out.println("再发货");
            }
        }
    }
}
class CustomerThread extends Thread{
    Object goods;
    Object money;

    public CustomerThread(Object goods, Object money) {
        this.goods = goods;
        this.money = money;
    }

    @Override
    public void run() {
        synchronized (goods) {
            System.out.println("先发货");
            synchronized (money){
                System.out.println("再给钱");
            }
        }
    }
}

在这里插入图片描述

线程通信使用同步方法实现
/*
    同步方法也有同步监视器对象:this,想要实现线程安全,this必须是相同的的
    线程通信方法,必须位于同步方法或者同步代码块内
*/
public class Test1 {
    public static void main(String[] args) {
        Bar1 bar1 = new Bar1();
        CookRunnable cookRunnable = new CookRunnable(bar1);
        Thread t0 = new Thread(cookRunnable, "厨师");
        WaiterRunnable waiterRunnable = new WaiterRunnable(bar1);
        Thread t1 = new Thread(waiterRunnable, "服务员1");
        Thread t2 = new Thread(waiterRunnable, "服务员2");
        t0.start();
        t1.start();
        t2.start();

    }
}
class CookRunnable implements Runnable{
    private Bar1 bar1;

    public CookRunnable(Bar1 bar1) {
        this.bar1 = bar1;
    }

    @Override
    public void run() {
        while (true){
            bar1.Put();
        }
    }
}
class WaiterRunnable implements Runnable{

    private Bar1 bar1;

    public WaiterRunnable(Bar1 bar1) {
        this.bar1 = bar1;
    }
    @Override
    public void run() {
        while (true){
            bar1.get();
        }
    }
}
class Bar1{
    public static final int MAX_NUM = 10;//吧台最多放10份菜
    int num;//统计菜数
    //厨师做菜
    public synchronized void Put(){
        if (num >= MAX_NUM) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"做了一道菜,现在有"+num+"道菜");
        //this.notify();
        this.notifyAll();
    }
    //服务员端菜
    public synchronized void get(){
        while (num<=0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num--;
        System.out.println(Thread.currentThread().getName()+"端走了一道菜,还有"+num+"道菜");
        this.notify();
    }
}

线程生命周期

  1. 观点1
    在这里插入图片描述
  2. 观点2
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值