线程调度 线程控制 多线程的启动方法2 卖票 线程同步 lock锁 生产者和消费者

91 阅读6分钟

线程调度

线程有两种调度模型

  • 分时调度模型:所有线程轮流使用cpu控制权,平均分配线程占用cpu的时间

  • 抢占式调度模型:优先让先级的线程使用cpu,如果线程优先级相同,那么就会随机选择一个优先级高的线程获取cpu的时间片相对多一些

  • Java使用的是抢占式调度模型

    • 随机性

    假如计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一

    定的

优先级相关方法

方法名说明
final int getPriority()返回此线程的优先级
final void setPriority(int newPriority)更改此线程的优先级,默认线程优先级是5;优先级范围是1-10
package Demo4;

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

Demo4B tp1 =new Demo4B();
Demo4B tp2 =new Demo4B();
Demo4B tp3 =new Demo4B();
tp1.setName("高铁");
tp2.setName("飞机");
tp3.setName("汽车");
//public final int getPriority返回线程优先级
        System.out.println(tp1.getPriority());
        System.out.println(tp2.getPriority());
        System.out.println(tp3.getPriority());
        //public final setPriority(int newPriority):更改此线程的优先级
        tp1.setPriority(5);
        tp2.setPriority(1);
        tp3.setPriority(10);
        tp1.start();
        tp2.start();
        tp3.start();
        System.out.println(Thread.MAX_PRIORITY);//10
        System.out.println(Thread.MIN_PRIORITY);//1
        System.out.println(Thread.NORM_PRIORITY);//5

    }
}

线程控制

方法名说明
static void sleep(long millis)使当前正在执行的线程停留(暂定执行,毫秒数)
void join()等待这个线死亡
void setDaemon(boolean on)将此项标记为守护线程,当运行的线程都是守护线程是,java虚拟机将退出
package Demo4;
//sleep
public class Demo4B extends Thread {
    public Demo4B(String name){super(name);}
    public Demo4B(){}
    @Override
    public  void run(){
        for (int i = 0; i <100 ; i++) {
            System.out.println(getName()+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}
package Demo4;
//join
public class Demo4A {
    public static void main(String[] args)  {

Demo4B tp1 =new Demo4B();
Demo4B tp2 =new Demo4B();
Demo4B tp3 =new Demo4B();
tp1.setName("康熙");
tp2.setName("大阿哥");
tp3.setName("二阿哥");
        tp1.start();
        try {
            tp1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        tp2.start();
        tp3.start();


    }
}
package Demo4;
//setDaemon
public class Demo4A {
    public static void main(String[] args)  {

Demo4B tp2 =new Demo4B();
Demo4B tp3 =new Demo4B();
tp2.setName("关羽");
tp3.setName("张飞");
//设置主线程为刘备
        Thread.currentThread().setName("刘备");
        //设置守护线程
        tp2.setDaemon(true);
        tp3.setDaemon(true);
        tp2.start();
        tp3.start();
for(int i = 0 ;i<10;i++){
    System.out.println(Thread.currentThread().getName()+":"+i);
}

    }
}
package Demo4;

public class Demo4B extends Thread {
    public Demo4B(String name){super(name);}
    public Demo4B(){}
    @Override
    public  void run(){
        for (int i = 0; i <100 ; i++) {
            System.out.println(getName()+i);

        }
    }

}

多线程的启动方法2

方式2:实现Runnable接口

  1. 定义一个类myrunnable实现runnable接口
  2. 在myrunnnble类总重写run方法
  3. 创建myrunnable类的对象
  4. 创建thread类的对象,把myrunnable对象作为构造方法的参数
  5. 启动线程

多线程的启动方式

  • 继承Thread类
  • 实现runnable接口

相比继承thread类,实现runnable接口的好处

  • 避免java单继承的局限性
  • 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码,数据有效的分离,体现了面向对象的设计思想
package Demo4;

public class Demo4A {
    public static void main(String[] args)  {
Runnable my = new Demo4B();
//Thread t1 = new Thread(my,);
//Thread t2 = new Thread(my);
        Thread t1 = new Thread(my,"高铁");
        Thread t2 = new Thread(my,"飞机");
t1.start();
t2.start();
    }
}
package Demo4;

public class Demo4B implements Runnable {
    @Override
    public  void run(){
        for (int i = 0; i <100 ; i++) {
            System.out.println(Thread.currentThread().getName()+" : "+i);

        }
    }

}

卖票

package Demo4;

public class Demo4B implements Runnable {
    int tickes = 100;
    Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (obj) {//进来后,就会将下面这个代码给锁死
                if (tickes > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + tickes + "张票");
                    tickes--;
                }
            }
        }
    }
}
package Demo4;

public class Demo4A {
    public static void main(String[] args)  {
Runnable my = new Demo4B();
//Thread t1 = new Thread(my,);
//Thread t2 = new Thread(my);
        Thread t1 = new Thread(my,"窗口1");
        Thread t2 = new Thread(my,"串口2");
        Thread t3 = new Thread(my,"窗口3");
t1.start();
t2.start();
t3.start();
    }
}

线程同步

同步方法:就是把synchronized关键字加到方法上

  • 格式

修饰符synchronized返回值类型 方法名 (方法参数){}

同步方法的对象:this

同步静态方法:就是把synchronize关键字加到静态方法上

  • 格式

修饰符 static synchronized 返回值类型 方法名(方法参数){}

同步静态方法的锁是什么呢?

类名.class

package Demo4;

public class Demo4B implements Runnable {
    private static int tickes = 100;
    private Object obj = new Object();
    private int x = 0;

    @Override
    public void run() {
        while (true) {
            if (x % 2 == 0) {
                synchronized (Demo4B.class) {//进来后,就会将下面这个代码给锁死
                    if (tickes > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + tickes + "张票");
                        tickes--;
                    }
                }
            }else sellTickes();
        }
    }

    private  static  synchronized void sellTickes() {
        if (tickes > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第" + tickes + "张票");
            tickes--;
    }
}
}
package Demo4;

public class Demo4A {
    public static void main(String[] args)  {
Runnable my = new Demo4B();
//Thread t1 = new Thread(my,);
//Thread t2 = new Thread(my);
        Thread t1 = new Thread(my,"窗口1");
        Thread t2 = new Thread(my,"串口2");
        Thread t3 = new Thread(my,"窗口3");
t1.start();
t2.start();
t3.start();
    }
}

lock锁

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5后提供了一个新的对象lock

lock实现提供比使用synchronize方法和语句可以获得跟广泛的锁定操作

lock中提供获得锁和释放锁的方法

  • void lock():获得锁
  • void unlock():释放锁

lock是接口不能被直接实例化,这里采用它的实现类ReentrantLock来实例化

Reentrantlock的构造方法

  • ReentrantLock():创建一个Reentaantlock的实例
package Demo4;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo4B implements Runnable {
    private static int tickes = 100;
    private Object obj = new Object();
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                if (tickes > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + tickes + "张票");
                    tickes--;
                }
            } finally {
                lock.unlock();
            }
        }
    }
}

生产者和消费者

概述

为了体现生产者和消费过程中的等待和唤醒,java就提供了及格方法让我们使用,这几个方法在Object雷总object类的等待和唤醒方法

方法名说明
void wait()导致当前线程等待,知道另一个线程调用该对象的notify()方法或notifyAll()方法
void notify()唤醒正在等待对象监视器的单个线程
void notifyAll()唤醒正在等待对象监视器的所有线程

案例需求

生产者消费者案例中包含的类:

奶箱类(Box):定义一个成员变量,表示第x瓶奶,提供存储牛奶和获取牛奶的操作

生产者类(Producer):实现Runnable接口,重写run()方法,调用存储牛奶的操作

消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶的操作

测试类(BoxDemo):里面有main方法,main方法中的代码步骤如下

①创建奶箱对象,这是共享数据区域

②创建消费者创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作

③对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作

④创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递

⑤启动线程

package Demo5;

import java.util.concurrent.Callable;

public class BoxDemo {
    public static void main(String[] args) {
        //创建奶箱对象
        Box b =new Box();
        //创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
        Producer p = new Producer(b);
        //创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
      Customer c =new Customer(b);
        //创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
        Thread t1 = new Thread(p);
        Thread t2 = new Thread(c);
        //启动线程
        t1.start();
        t2.start();
    }
}
package  Demo5;
public class Box{
    //定义一个成员变量,表示第x瓶奶
    private  int milk;
    //奶箱状态
    private boolean state = false;
    //提供储存牛奶和获取牛奶的操作
    //如果有奶,等待消费
    public synchronized void put(int milk){
        if(state) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果没有,就生产奶
        this.milk=milk;
        System.out.println("送奶工将第" + this.milk + "瓶奶放入奶箱");
        //生产完毕之后,修改奶箱状态
        state = true;
        //唤醒其他等待线程
        notifyAll();
    }
    public synchronized void get(){
        //无奶,就生产
        if(!state){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //有,就消费
        System.out.println("用户拿到第" + this.milk + "瓶奶");
        //消费完毕之后,修改奶箱状态
        state = false;
        //唤醒其他等待线程
        notifyAll();
    }
}
package Demo5;

public class Producer implements  Runnable{
    private Box b ;
    public Producer(Box b ) {
        this.b = b;
    }
    @Override
            public void run(){
            for (int i = 1; i <=20; i++) {
                b.put(i);
            }
        }
}
package Demo5;

public class Customer implements  Runnable{
    private Box b ;
    public Customer(Box b ){this.b=b;}
    @Override
    public void run(){
        while(true){
            b.get();
        }
    }
}