java第十六课(线程,多线程,Thread,Lock,线程通信)

1.了解线程

1.程序,进程,线程之间的区别

程序
为完成特定任务,用某种语言编写的一组指令的集合,就是一段静态代码(安装在电脑上的文件键)

进程
也是程序
但是是运行中的程序 是操作系统进行资源分配的最小单位

线程
线程可以进一步细化为进程,进程中的一个最小执行单元就是线程,是cpu进行调度的最小单元
 

2.进程和线程之间的关系

一个进程中可以包含多个线程(qq有多个聊天窗口)
一个线程只能属于一个进程
每个进程中至少包含一个线程(主线程java的Main方法就是用来启动主线程的)
在主线程中可以创建并启动其他线程
一个进程中的线程共享该进程的内存资源

2.创建线程

1.继承Thread,重写里面的run方法,但是在创建对象的时候不可以直接调用run方法,否则和普通的调用没什么区别,java中,main方法就是一个单线程

public class MyThread  extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(1);
        }
    }

    public static void main(String[] args) {
        MyThread my =new MyThread();
        my.run();//不是单线程,是简单的方法调度

       my.start();
        for (int i = 0; i < 20; i++) {
            System.out.println(2);
        }
    }
}

 1.Java中创建线程的两种方法

java中创建线程方式1(继承Thread类)
写一个类继承java.lang.Thread
重写run方法,线程中要执行的任务都要写在run方法中,或者在run方法中进行调用,启动时调用继承类中的start方法,这个方法会自己找到run方法并且启动

java中创建线程的方式2(实现Runable接口)
只先创建线程要执行的任务,创建一个类,实现Runable接口,重写执行任务中的run。优点:可以继承其他类和实现其他接口,适合多线程同时处理同一份资源时使用

public class MyThread1 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(1);
        }
    }
    public static void main(String[] args) {
        //创建任务
        MyThread1 myThread = new MyThread1();
        //创建线程
        Thread thread = new Thread(myThread);
        //启动线程
        thread.start();
        for (int i = 0; i < 20; i++) {
            System.out.println(2);
        }
    }
}

3.线程常用 API

run():定义线程要执行的代码
start():启动线程
currentThread():获取当前线程
getId():获取线程id
getName():获取线程名字
setName():为线程设置名字
getPriority():获取线程优先级
setPriority(2):设置线程优先级(1~10)为操作系统调度算法
getState():获取线程状态
sleep():让线程休眠指定时间
join():等待线程终止,调用了join()方法的线程执行完毕其他线程在执行
yield():线程主动退出cpu胆识不会造成阻塞

4.线程的生命周期

新建:刚刚建立了线程对象,并没有启动
就绪:调用start()后,线程就进入到了就绪状态
进入操作系统调度队列
运行状态: 获得cpu执行权,进入cpu执行
阻塞状态,例如调用了sleep(),join(),调用了Scanner输入
线程生命周期图。

5.多线程

什么时候需要多线程
       程序需要同时执行两个任务或者多个任务
       程序需要实现一些等待任务时如用户输入,文件读写,网络操作
多线程的优点
       提高程序cpu利用率,提升了效率
       改善程序结构,将一个大的任务拆分成若干个小任务
多线程的缺点
       线程多了占用内存,
       线程之间同时对资源共享的访问会互相影响,如果不加以控制会导致数据出错

如何解决多线程操作数据共享的问题
       排队+锁     在关键步骤,多个线程只能一个一个的执行

多线程同步
       多个线程同时读写同一份数据,可能会引起冲突,所以引入多线程同步机制,即各个线程有先来后到之分。

同步就是锁+排队
      几个线程要排队,一个一个对共享资源进行操作,而不是同时进行操作,
      为了保证数据在访问时的正确性,在访问时加入了锁机制

买票·问题

public class TicketThread implements Runnable {
    static int num=10;
    @Override
    public void run() {
        while (true){
            if (num>0){
                System.out.println(Thread.currentThread().getName()+"买到了"+num+"张票");
                num--;

            }else {
                System.out.println(Thread.currentThread().getName()+"票已经卖完了");
                break;
            }
        }
    }
}

public class TicketTest {
    public static void main(String[] args) {
        TicketThread t = new TicketThread();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.setName("窗口一");
        t2.setName("窗口二");
        t1.start();
        t2.start();
    }
}

出现问题票会重复被购买,原因是两个线程启动后进行num--前,另一个已经进入到循环中进行抢票。 

线程同步
      确保一个时间点只有一个线程访问共享资源,可以给共享资源加一把锁,哪一个线程获得了这把锁,哪一个线程才有权力访问该部分资源

synchronized(同步锁)
同步锁对象作用:用来记录有没有线程进入同步代码块中,如果有线程进入到同步代码块中,其他线程就不能进入到同步代码块中,直到上一个线程完成同步代码块里面的代码,其他线程才可以进入。
同步锁对象要求:同步锁对象必须是唯一的(多个线程拿到同一个对象)可以是任意一个对象,但是必须是唯一的。

synchronized修饰
synchronized修饰方法时,同步对象不需要我们指定,同步对象会默认提供,可以在synchronized前面加static表示静态的方法,
1.默认提供this(非静态方法)
2.静态方法(锁对象是当前类的class对象)

ReentranLock类实现了Lock,它拥有与synchronization相同的并发性和内存语义,在线程安全中可以显示加锁释放锁
ReentranLock加锁(Lock)和释放锁(UnLock)

ReentrantLock与synchronized的区别
synchronized是一个关键字,依靠底层编译后的指令去执行,可以修饰一个方法,还可以修饰代码块,是隐式加锁,代码执行完或者出现异常可以自己开锁
ReentranLock是一个类,依靠java代码去控制(底层是一个同步队列),只能修饰代码块,需要手动解锁,尽量将释放锁写在finally中,一但代码运行完或者出现异常,可以及时释放锁。

如何加锁
      synchronized关键字 
          修饰方法:会自动提供,非静态方法锁对象默认是this,静态方法锁默认对象是当前类的class类的对象
      ReentranLock类
           修饰代码块


线程通讯
   多个线程相互牵制,相互调度,多个线程之间相互作用。
      涉及三个方法
            wait():执行次方法线程进入阻塞,只有通过同步锁对象调用notify()方法才可以从阻塞状态出来
            notify():执行此方法就会唤醒一个被wait()的一个线程,如果有多个线程被wait()就会优先唤醒优先级高的
            notifyAll():唤醒所有被wait的线程
注意:wait(),notify(),nootifyAll()只能写在同步代码块或者同步方法中,否则会报错。
 

经典例题

生 产 者 ( P r o d u c t o r ) 将 产 品 放 在 柜 台 ( C o u n t e r ) , 而 消 费 者 ( C u s t o m e r ) 从 柜 台
处 取 走 产 品 , 生 产 者 一 次 只 能 生 产 固 定 数 量 的 产 品 ( 比 如 : 1 ) , 这 时 柜 台 中 不 能
再 放 产 品 , 此 时 生 产 者 应 停 止 生 产 等 待 消 费 者 拿 走 产 品 , 此 时 生 产 者 唤 醒 消 费 者 来
取 走 产 品 , 消 费 者 拿 走 产 品 后 , 唤 醒 生 产 者 , 消 费 者 开 始 等 待 

消费者:

public class CustomerThread extends Thread{
    Counter c  ;
    public CustomerThread(Counter c){
        this.c=c;
    }
    @Override
    public void run() {

        while (true){
            try {
                c.sub();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

生产者:

public class ProductorThread extends Thread{
    Counter c ;
    public ProductorThread(Counter c){
        this.c=c;
    }
    @Override
    public void run() {
       while (true){
           try {
               c.add();
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               throw new RuntimeException(e);
           }
       }
    }
}

柜台:

public class Counter {
    int num=0;
    Random r =new Random();
    public synchronized void add() throws InterruptedException {
        if (num==0){
            this.notify();
            num=r.nextInt(9)+1;
            System.out.println("生产者生产了"+num+"个食品");
        }else {
            this.wait();
        }
    }
    public synchronized void sub() throws InterruptedException {
        if (num>0){
            this.notify();
            num--;
            System.out.println("消费者拿走了"+1+"个食品,还剩"+num);
        }else {
            this.wait();
        }
    }
}

创建生产者消费者还有柜台对象 

public class CounterTest {
    public static void main(String[] args) {
        Counter c =new Counter();
        CustomerThread c1= new CustomerThread(c);
        ProductorThread p1 =new ProductorThread(c);
        c1.start();
        p1.start();
    }
}

 

 

 


 


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值