java多线程

process进程 thread线程

三种创建方式

Thread class 继承Thread类

创建线程方式一:继承Thread类,重写run()方法,调用start开启线程

继承Thread类,首先重写run方法,然后在主程序中创建一个线程对象,调用start方法开启线程

线程开启不一定立即执行,由CPU调度

理解:在主程序中先实例化Thread的继承类,由于继承了Thread,所以可以直接使用他实例化出来的对象的start方法来开启线程

不建议使用:避免OOP单继承局限性

Runnable接口 实现Runnable接口

创建线程方式2:实现2runnable接口,重写run方法,执行线程需要丢入runnable的实现类,调用start方法

作为Runnable接口的实现类,首先重写run方法,然后再主程序中创建一个runnable接口的实现类,然后创建线程对象,通过线程对象来开启线程,需要用到代理

理解:在主程序中实例化runnable接口的实现类,由于没有继承Thread,所以要实例化Thread。然后运用Thread实例化出来的对象的start方法

推荐使用:避免单继承局限性,方便于同一对象被多个线程使用

TestThread3 testThread3 = new TestThread3();
Thread thread = new Thread(testThread3);
thread.start();
//Thread实例化时要把实现类投入Thread()中
//后两句可以合并为
new Thread(实现类对象名,name).start( )//name类似于对象.name,借此实现同一对象被多个线程使用

Callable接口 实现Callable接口

了解即可

静态代理

真实对象与代理对象都要实现同一个接口

代理对象要代理真实角色,需要将真实角色传入。然后调用代理对象的方法

好处:代理对象可以做很多真实对象做不了的事情,真实对象可以做自己的东西

Lambda表达式

使用目的:避免匿名内部类定义过多,可以让代码更简洁,去掉了没有意义的代码只留下了核心逻辑

函数式接口

任何接口,如果只包含唯一一个抽象方法,那么他就是函数式接口(Runnable就是)

public interface Runnable{
    public abstract void run();
}
//在接口里,void ilike默认包含public abstract

对于函数式接口,我们可以通过lambda表达式来创建该接口的对象

对于一个方法的简化进化史

  1. 定义一个函数式接口,然后用实现类
  2. 为避免在主类外部,采用在主类内部使用静态内部类
  3. 为避免在主方法外部,采用在主方法内部使用局部内部类
  4. 为避免命名类,采用匿名内部类(没有类的名称,必须借助接口或者父类实现,最好要加分号)
  5. 由于只有一个方法,为避免多余的无意义代码,采用lambda表达式
like = ()->{
            System.out.println("I like lambda5");
        };
        like.Lambda();

lambda表达式简化

lambda表达式只能有一行代码时才能简化,不然要用代码块包裹

  1. 可以去掉参数类型(多个也可以去掉,要去掉就都去掉)
  2. 可以去掉括号,只能是只有一个参数时才可以
  3. 可以去掉花括号,
ILove love=(int a)-> {
                System.out.println("i love you"+a);
            };
//在前面ILove love = null;就可以省去ILove
love=(a)-> {
            System.out.println("i love you"+a);
        };
      
love=(a)-> System.out.println("i love you"+a);
 

线程的五大状态

创建状态:线程对象的创建 new

就绪状态:当调用start()方法,线程进入就绪状态,但是不会立刻被调度执行

阻塞状态:当调用sleep,wait或同步锁定时,进入阻塞状态

运行状态:运行时,线程才真正执行线程体的代码块

死亡状态:线程中断或者结束,一旦进入死亡状态就不能再次启动

线程方法

setPriority(int newPriority) 更改线程的优先级

static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠

void join( ) 等待该线程终止

static void yield( )暂停当前正在执行的线程对象,并执行其他线程

void interrupt( ) 中断线程,别用这个方法

boolean isAlive( ) 测试线程受处于活动状态

停止线程

  • 不推荐使用JDK提供的stop( ),destroy( )方法
  • 推荐线程自己停止下来
  • 建议使用一个标志位进行终止变量,当flag=false,则终止线程运行

线程休眠_sleep

  • sleep(时间)指定当前线程阻塞的毫秒数
  • sleep存在异常InterruptedException
  • sleep时间达到后线程进入就绪状态
  • sleep可以模拟网络延迟,倒计时
  • 每个对象都有一个锁,sleep不会释放锁

模拟网络延迟可以放大问题的发生性

线程礼让_yield

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 让线程从运行状态转为就绪状态
  • 让CPU重新调度,礼让不一定成功,要看CPU

线程强制执行_join

  • join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
  • 可以想象为插队

运行Thread对象vip方法vip.join( )的是被插队的一方,先让vip对象线程执行结束

观察线程状态

get.State( )获取当前状态

state!=Thread.State.TERMINATED 判断变量state是不是TERMINATED

线程优先级

优先级范围是1-10 数字越大,优先级越高

优先级高,不一定先执行,看CPU。但是权重会更大一点

守护(daemon)线程

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

thread.setDeamon(true)设置线程为守护线程,默认为false表示用户线程

用户线程执行完后需要一段时间虚拟机才会停止

线程同步

并发:同一对象被多个线程同时操作

形成条件:队列+锁(synchronized) 才能保证安全性

问题:多线程竞争下损失性能。 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题

不加锁:多个线程获得一样的内存,都认为可以取票,导致最后剩余票数为负数

同步方法

synchronized相当于一个修饰符。可以生成同步方法和同步块

缺陷:将一个大的方法声明为synchronized会影响效率

方法里面需要修改的内容才需要锁

synchronized同步方法锁的其实是this

以下为一个取钱的代码,不进行同步的话会存在错误

同步方法 加上synchronized修饰方法

同步块synchronized(同步监视器){ 代码 } 同步监视器可以是任何对象,推荐使用共享资源作为同步监视器

同步方法的同步监视器就是this,就是这个对象本身或者是class

如果用同步方法锁住run方法,实际上是synchronized(run),代指的是他所在的类Drawing。Drawing有两个对象you和girl,并不是被多个线程同时操作的对象,且Drawing类不涉及增删改,所以没有用(举实际离子的话就是把银行锁住了,两个人排队进了银行,但是没有解决取钱冲突的问题。应该锁住他们两个同时操作的银行卡账户)。

所以采用同步块,将变化的对象account锁住,同时将关于account增删改的代码移入同步块中

package demo04;

public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"结婚基金");

        Drawing you = new Drawing(account,50,"你");
        Drawing girl = new Drawing(account,100,"GIRL");

        you.start();
        girl.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 darwingMoney;
    int nowMoney;

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

    @Override
    public void run() {
        if(account.money-darwingMoney<0){
            System.out.println(Thread.currentThread().getName()+"钱不够,去不了");
            return;
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        account.money-=darwingMoney;

        nowMoney+=darwingMoney;

        System.out.println(account.name+"余额为:"+account.money);
        System.out.println(this.getName()+"手里的钱:"+nowMoney);
    }
}
//把run中的增删改代码放入同步块中
@Override
    public void run() {
        synchronized (account){
            if(account.money-darwingMoney<0){
                System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
                return;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            account.money-=darwingMoney;

            nowMoney+=darwingMoney;

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

死锁

多个线程各自占有一些共享资源,并且相互等待其他线程占有的资源才能运行,就会导致两个以上的线程都在等待对方释放资源。即某一个同步块同时拥有两个以上对象的锁。

if (choice == 0) {
            synchronized (lipstick) {//获得口红的锁
                System.out.println(this.girlname + "获得口红的锁");
                Thread.sleep(1000);
            }
            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 + "获得口红的锁");

这样不会出现死锁,choic为0的情况获得口红锁将其执行完,然后释放口红锁。

如果将镜子锁写到口红锁内部,那么在执行完输出口红语句后由于无法获得已经被else语句执行体锁上的镜子所以无法走完程序,进而无法释放口红锁

Lock锁(接口)

//定义Lock锁
    private final ReentrantLock lock=new ReentrantLock();
lock.lock();//加锁
try{
               
}finally {
    //解锁
     lock.unlock();
}

lock是显式锁,要手动开启和关闭

lock只有代码块锁,没有方法锁

优先使用顺序:lock>同步代码块>同步方法

线程协作 生产者消费者模式

wait( )线程一直等待,直到其他线程通知,与sleep不同,会释放锁

wait( long timeout) 指定等待的毫秒数

notify( ) 唤醒一个处于等待状态的线程

notifyAll( ) 唤醒同一个对象上所有调用wait方法的线程,优先级别高的线程优先调度

1.管程法 设置一个数据缓存区,生产者将生产好的数据放入缓存区中,消费者从缓存区拿出数据

线程池

corePoolSize:核心池大小

maximumPoolSize:最大线程数

keepAliveTime:线程没有任务时最多保持多长时间后会终止

目前讲的不是很清晰

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值