线程调度
线程有两种调度模型
-
分时调度模型:所有线程轮流使用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接口
- 定义一个类myrunnable实现runnable接口
- 在myrunnnble类总重写run方法
- 创建myrunnable类的对象
- 创建thread类的对象,把myrunnable对象作为构造方法的参数
- 启动线程
多线程的启动方式
- 继承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();
}
}
}