线程知识:
synchronized
synchronized 用在方法签名上(以test为例),当某个线程调用此方法时,会获取该实例的对象锁,方法未结束之前,其他线程只能去等待。当这个方法执行完时,才会释放对象锁。其他线程才有机会去抢占这把锁,去执行方法test,但是发生这一切的基础应当是所有线程使用的同一个对象实例,才能实现互斥的现象。否则synchronized关键字将失去意义。
使用synchronized代码块,可以只对需要同步的代码进行同步,这样可以大大的提高效率。
synchronized(obj){
//todo code here
}
wait()
释放占有的对象锁,线程进入等待池,释放cpu,而其他正在等待的线程即可抢占此锁,获得锁的线程即可运行程序。
sleep()
线程调用此方法后,会休眠一段时间,休眠期间,会暂时释放cpu,但并不释放对象锁。也就是说,在休眠期间,其他线程依然无法进入此代码内部。休眠结束,线程重新获得cpu,执行代码。
notify()
该方法会唤醒因为调用对象的wait()而等待的线程,其实就是对对象锁的唤醒,从而使得wait()的线程可以有机会获取对象锁。调用notify()后,并不会立即释放锁,而是继续执行当前代码,直到synchronized中的代码全部执行完毕,才会释放对象锁。
notifyAll()
唤醒所有等待的线程。
wait() 与notify()/notifyAll()都是Object的方法,并不是线程的方法!
关键问题
一
Runnable定义的子类中没有start()方法,只有Thread类中才有。
在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处:
避免点继承的局限,一个类可以继承多个接口。
二
先明白两个概念:锁池和等待池。synchronized是锁池,wait、notify、notifyAll是等待池。等待池的对象是不会竞争锁的,当notifyAll后,等待池中的线程会被唤醒进入到该线程的锁池中重新竞争对象锁,重新获得锁后的对象会从wait后继续执行代码,其他对象会被阻塞,而不是wait。被阻塞的对象会等待下一次被唤醒(notify、notifyAll)。另外,notify不是线程安全的,notifyAll才是。
适合于资源的共享
题目
生产者:向盒子里放苹果
消费者:从盒子里取苹果
盒子:苹果数量不超过5
这个问题用线程来实现
先建立一个苹果类
给生成的苹果一个编号
public class Apple {
//苹果编号
int id;
public Apple(int id) {
this.id = id;
}
}
盒子
public class Box {
//装苹果的盒子 为什么是链表 因为 它有boxs.removeFirst();方法 方便取出苹果
public LinkedList<Apple> boxs =new LinkedList<Apple>();
public List<Apple> getBoxs() {
return boxs;
}
public void setBoxs(LinkedList<Apple> boxs) {
this.boxs = boxs;
}
public synchronized void push(Apple apple,String threadName){
//盒子容量5
while (boxs.size()==5){
try {
System.out.println(threadName+":盒子满了,放不下(进入等待状态),快叫消费者来消费");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//唤醒所有等待线程 notifyAll();安全 ---notify()不安全
this.notifyAll();
//苹果放进盒子中
System.out.println(threadName+"生产苹果"+apple.id);
boxs.add(apple);
try {
//线程等待0.5秒便于观察
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void pop(String threadName){
while (boxs.size()==0){
try {
System.out.println(threadName+":盒子没苹果了(进入等待状态),快叫生产者来生产");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//唤醒所有等待线程 notifyAll();安全 ---notify()不安全
this.notifyAll();
//盒子中拿去第一个苹果
System.out.println(threadName+"消费苹果");
boxs.removeFirst();
try {
//线程等待0.5秒便于观察
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
消费者
public class Consumer implements Runnable {
Box box=null;
public Consumer(Box box) {
this.box = box;
}
@Override
public void run() {
while (true) {
//互斥
synchronized (Consumer.class) {
box.pop(Thread.currentThread().getName());
}
}
}
}
生产者
public class Producer implements Runnable{
public static Integer count = 0;
Box box = null;
public Producer(Box box) {
this.box = box;
}
public void run() {
while (true) {
synchronized (Producer.class) {
count++;
//生产者给苹果编号
Apple apple =new Apple(count);
box.push(apple,Thread.currentThread().getName());
}
}
}
}
测试类
public class AppleThreadTest {
public static void main(String[] args) {
//盒子
Box box =new Box();
//生产者
Producer p1=new Producer(box);
Producer p2=new Producer(box);
//消费者
Consumer c1 =new Consumer(box);
Consumer c2 =new Consumer(box);
//创建线程
Thread t1=new Thread(p1,"生产者——小刘");
Thread t2=new Thread(p2,"生产者——小李");
Thread t3=new Thread(c1,"消费者——老刘");
Thread t4=new Thread(c2,"消费者——老李");
//启动线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果:
我们发现都是盒子里没苹果了,为什么呢?
因为一共4个线程2个生产者2个消费者,优先级都一样 加减基本维持在0
我们可以改变其中一个的优先级
c2.setPriority(10);
java 中的线程优先级的范围是1~10,默认的优先级是5。