文章目录
多线程
线程的通信—打印机打印–不断的输入不断的输出
- 分析:
- 需要两个线程—输入线程和输出线程
- 需要两个任务—输入任务和输出任务
- 需要一份数据
public class Demo2 {
public static void main(String[] args) {
//1.创建数据了对象
Des des = new Des();
//2.创建任务类对象并绑定数据
Input input = new Input(des);
Output output = new Output(des);
//3.创建线程
Thread inputThread = new Thread(input);
Thread outputThread = new Thread(output);
//4.开启线程
inputThread.start();
outputThread.start();
}
}
//创建数据类
class Des{
String name;
String sex;
}
//创建输入任务
class Input implements Runnable{
Des des;
public Input(Des des) {
this.des = des;
}
public void run() {
/*
* 分析:需要输入任务与输出任务共用一把锁,保证两个任务之间是同步的.
* 给两个任务加一把锁,锁可以是des或Object.class
*
* 不建议使用Object.class,原因:由于Object的使用范围太大,会造成不必要的错误
* 使用des最合适,因为他只被两个线程共享.
*
* 注意:只给一个任务加锁,无法实现两个线程的同步.
*/
int i= 0;
while (true) {
synchronized (des) {
if (i==1) {
des.name = "凤姐";
des.sex = "男";
}else {
des.name = "芙蓉姐姐";
des.sex = "女";
}
i=(i+1)%2;
}
}
}
}
//创建输出任务
class Output implements Runnable{
Des des;
public Output(Des des) {
this.des = des;
}
public void run() {
while (true) {
synchronized (des) {
System.out.println("姓名:"+des.name+" 性别:"+des.sex);
}
}
}
}
线程的通信—实现:打印机打印–一次输入一次输出
-
分析:
- 需要两个线程—输入线程和输出线程
- 需要两个任务—输入任务和输出任务
- 需要一份数据
-
使用唤醒等待机制实现:wait()/notify()/notifyall()等
-
wait():让当前的线程进入等待的状态,他会被放入一个池子(线程池),失去了抢cpu的能力,等待唤醒.(锁—相当于给当前的线程做了一个标记)
-
notify():让当前的线程从等待线程唤醒,相当于从池子中取出线程.(唤醒的是同一把锁下的任意一个线程)
-
notifyall():唤醒的是同一把锁下的所有线程.
public class Demo3 {
public static void main(String[] args) {
//1.创建数据了对象
Des1 des = new Des1();
//2.创建任务类对象并绑定数据
Input1 input = new Input1(des);
Output1 output = new Output1(des);
//3.创建线程
Thread inputThread = new Thread(input);
Thread outputThread = new Thread(output);
//4.开启线程
inputThread.start();
outputThread.start();
}
}
//创建数据类
//封装类的原则:是你的活儿你做,不是你的活儿不要做.
class Des1{
String name;
String sex;
boolean flag = false;//创建一个标识,控制唤醒与等待的切换
//获取数据--处理输入
public synchronized void setData(String name,String sex) {
if (flag == true) {
try {//让输入线程等待,当flag值为true的时候
//在执行代码的时候,这里对应的是哪个线程,锁对象操作的就是哪个线程.
wait();//只要一致性wait,线程会立刻停在这里,等待下次唤醒.
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name;
this.sex = sex;
flag = !flag;
//唤醒输出线程
//当执行唤醒的时候,在线程池中没有找到被当前的锁标记的线程,我们称为空唤醒,空唤醒对程序没有影响,程序允许空唤醒.
notify();
}
//将数据打印到控制台-处理输出
public synchronized void getData(){
if (flag == false) {
try {
wait();//让输出线程处于等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("姓名:"+name+" 性别:"+sex);
flag = !flag;
notify();//将输入线程唤醒
}
}
//创建输入任务
class Input1 implements Runnable{
Des1 des;
public Input1(Des1 des) {
this.des = des;
}
public void run() {
/*
* 分析:需要输入任务与输出任务共用一把锁,保证两个任务之间是同步的.
* 给两个任务加一把锁,锁可以是des或Object.class
*
* 不建议使用Object.class,原因:由于Object的使用范围太大,会造成不必要的错误
* 使用des最合适,因为他只被两个线程共享.
*
* 注意:只给一个任务加锁,无法实现两个线程的同步.
*/
int i= 0;
while (true) {
if (i == 1) {
des.setData("凤姐", "男");
}else {
des.setData("芙蓉姐姐", "女");
}
i=(i+1)%2;
}
}
}
//创建输出任务
class Output1 implements Runnable{
Des1 des;
public Output1(Des1 des) {
this.des = des;
}
public void run() {
while (true) {
des.getData();
}
}
}
多线程的设计模式:生产者消费者
两类:
- 单生产者单消费者
- 多生产者多消费者
单生产者单消费者
- 分析:
- 生产线程,消费线程
- 生产任务,消费任务
- 产品
public class Demo4 {
public static void main(String[] args) {
//1.创建产品
Product product = new Product();
//2.创建生产任务,消费任务
Producer producer = new Producer(product);
Consumer consumer = new Consumer(product);
//3.创建生产线程,消费线程
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
//4.开启线程
t1.start();
t2.start();
}
}
//创建产品类
class Product{
String name;
double price;
int count;
boolean flag = false;//用来在唤醒与等待之间进行切换
//为生产准备数据
public synchronized void setProduce(String name,double price){
if (flag == true) {
try {
wait();//让生产线程等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name;
this.price = price;
System.out.println(Thread.currentThread().getName()+" 生产了:"+name+" 产品的数量:"+count+" 产品的价格:"+price);
count++;
flag = ! flag;
notify();//唤醒消费线程
}
//为消费准备数据
public synchronized void consume() {
if (flag == false) {
try {
wait();//让消费线程等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 消费了:"+name+" 产品的数量:"+count+" 产品的价格:"+price);
flag = ! flag;
notify();//唤醒生产线程
}
}
//创建生产任务
class Producer implements Runnable{
Product product;
public Producer(Product product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.setProduce("bingbing", 10);
}
}
}
//创建消费任务
class Consumer implements Runnable{
Product product;
public Consumer(Product product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.consume();
}
}
}
先研究多生产者多消费者
- 分析:
- 生产线程,消费线程有多个
- 生产任务,消费任务各有一个
- 产品一个
public class Demo5 {
public static void main(String[] args) {
//1.创建产品
Product1 product = new Product1();
//2.创建生产任务,消费任务
Producer1 producer = new Producer1(product);
Consumer1 consumer = new Consumer1(product);
//3.创建生产线程,消费线程
Thread t0 = new Thread(producer);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
Thread t3 = new Thread(consumer);
//4.开启线程
t0.start();
t1.start();
t2.start();
t3.start();
}
}
//创建产品类
class Product1{
String name;
double price;
int count;
boolean flag = false;//用来在唤醒与等待之间进行切换
//为生产准备数据
public synchronized void setProduce(String name,double price){
while (flag == true) {
try {
wait();//让生产线程等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name;
this.price = price;
System.out.println(Thread.currentThread().getName()+" 生产了:"+name+" 产品的数量:"+count+" 产品的价格:"+price);
count++;
flag = ! flag;
//notify();//唤醒消费线程
notifyAll();
}
//为消费准备数据
public synchronized void consume() {
while (flag == false) {
try {
wait();//让消费线程等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 消费了:"+name+" 产品的数量:"+count+" 产品的价格:"+price);
flag = ! flag;
//notify();//唤醒生产线程
notifyAll();
}
}
//创建生产任务
class Producer1 implements Runnable{
Product1 product;
public Producer1(Product1 product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.setProduce("bingbing", 10);
}
}
}
//创建消费任务
class Consumer1 implements Runnable{
Product1 product;
public Consumer1(Product1 product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.consume();
}
}
}
Lock:
-
比较synchronized和Lock
-
synchronized:从jdk1.0开始使用----隐式同步
synchronized(锁对象){//获取锁 我们将这里的锁称为锁旗舰或者监听器同步的代码
}//释放锁 -
Lock:从jdk1.5开始使用----显示同步
- 原理:Lock是接口,我们要通过他的子类工作
- 具体的工作流程:
- 首先调用lock的lock()方法,获取锁
- 进行同步操作的代码
- 调用lock的unlock()方法,释放锁
-
-
使用场景总结:
- 当进行多生产者的消费者的时候,使用lock,其他的使用synchronized
-
使用效率上lock比synchronized高.
public class Demo6 {
public static void main(String[] args) {
//1.创建产品
Product2 product = new Product2();
//2.创建生产任务,消费任务
Producer2 producer = new Producer2(product);
Consumer2 consumer = new Consumer2(product);
//3.创建生产线程,消费线程
Thread t0 = new Thread(producer);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
Thread t3 = new Thread(consumer);
//4.开启线程
t0.start();
t1.start();
t2.start();
t3.start();
}
}
//创建产品类
class Product2{
String name;
double price;
int count;
boolean flag = false;//用来在唤醒与等待之间进行切换
//创建Lock的对象
Lock lock = new ReentrantLock();
//创建用于生产线程的Condition对象
Condition proCon = lock.newCondition();
//创建用于消费线程的Condition对象
Condition conCon = lock.newCondition();
//为生产准备数据
public void setProduce(String name,double price){
try {
lock.lock();//获取锁
while (flag == true) {
try {
//wait();//让生产线程等待
proCon.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name;
this.price = price;
System.out.println(Thread.currentThread().getName()+" 生产了:"+name+" 产品的数量:"+count+" 产品的价格:"+price);
count++;
flag = ! flag;
//notify();//唤醒消费线程
//notifyAll();
conCon.signal();
} finally {//必须执行的代码
lock.unlock();//释放锁
}
}
//为消费准备数据
public void consume() {
try {
lock.lock();
while (flag == false) {
try {
//wait();//让消费线程等待
conCon.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 消费了:"+name+" 产品的数量:"+count+" 产品的价格:"+price);
flag = ! flag;
//notify();//唤醒生产线程
//notifyAll();
proCon.signal();
} finally {
lock.unlock();
}
}
}
//创建生产任务
class Producer2 implements Runnable{
Product2 product;
public Producer2(Product2 product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.setProduce("bingbing", 10);
}
}
}
//创建消费任务
class Consumer2 implements Runnable{
Product2 product;
public Consumer2(Product2 product) {
super();
this.product = product;
}
public void run() {
while (true) {
product.consume();
}
}
}
守护线程
相当于后台线程,依赖于前台线程.正常情况下,当前台线程结束的时候,不管守护线程有没有结束,都会立刻结束.
- 当程序调用setDaemon的时候,并且将参数设置成true,他就变成了守护线程.
- 典型的守护线程:垃圾回收线程
- 注意:这个方法一定要在start方法之前调用
public class Demo7 {
public static void main(String[] args) {
Test test = new Test();
Thread thread = new Thread(test);
/*
* 当程序调用setDaemon的时候,并且将参数设置成true,他就变成了守护线程.
* 注意:这个方法一定要在start方法之前调用
*/
thread.setDaemon(true);
thread.start();
int i=0;
while (true) {
if (i++ == 10) {
System.out.println(Thread.currentThread().getName()+" i:"+i);
break;//主线程结束
}
}
}
}
class Test implements Runnable{
public void run() {
while (true) {
System.out.println("守护线程");
}
}
}
join()方法:
- 原理:程序一旦调用了join方法,他的优先级会高于主线程.意思是说,主线程会等当前线程执行完后再去执行.
- 注意点:这个线程的优先级只比main高,对其他的线程没有影响.
- 当线程开始工作后,让thread0调用join方法,他的优先级会高于main线程.
- join方法必须在线程开始工作后执行.