多线程
(看传值博客视频记录下来点点滴滴传值博客)
多线程安全问题产生的前提
- 多个线程操作同一个共享变量
- 操作共享变量的代码超过2行
解决多线程安全问题方法
- 用同步代码块
- 同步函数
注意必须保证线程用的是同一个锁这里锁可以是任何对象
同步函数块的锁是this
静态同步函数的锁是该类的字节码对象
死锁的产生
如果有run方法中有两个不同的锁A、B,一个线程先获得A锁,然后在请求B锁,现在另外一个线程同时获得了B锁,在请求A锁,那么就会发生死锁
代码部分后续会补上
多线程通讯
1、引入等待唤醒机制
package com.hfview.thread;
/**
* 引出多线程通讯的问题
* 目标:生成一个名字,然后输出一个名字
* 不加入同步会出现名字和性别对应不上
* 不加入等待唤醒机制 生成的名字不会立马输出,而是连续的一片
* @author zhw
*/
/*共享资源*/
class Factory{
public String name;
public String sex;
}
/* 给factory赋值任务*/
class Input implements Runnable{
Factory f;
public Input(Factory factory){
this.f=factory;
}
public void run() {
int num=0;
while(true){
synchronized (Factory.class) {
if(num==0){
f.name="zhanghongwei";
f.sex="男男男男男";
}else{
f.name="hwj";
f.sex="女";
}
}
num=(num+1)%2;
}
}
}
/* 给factory输出任务*/
class Output implements Runnable{
Factory f;
public Output(Factory factory){
this.f=factory;
}
public void run() {
while (true) {
synchronized (Factory.class) {
System.out.println("name:" + f.name + " sex:" + f.sex);
}
}
}
}
/*主函数*/
public class ThreadMsg1 {
public static void main(String[] args) {
Factory factory =new Factory();
Input in=new Input(factory);
Output out =new Output(factory);
Thread t1=new Thread(in);
Thread t2=new Thread(out);
t1.start();
t2.start();
}
}
2、利用等待唤醒机制解决问题
package com.hfview.thread;
/*
等待/唤醒机制。
涉及的方法:
1,wait(): 让线程处于冻结状态,被wait的线程会被存储到线程池中。
2,notify():唤醒线程池中一个线程(任意).
3,notifyAll():唤醒线程池中的所有线程。
这些方法都必须定义在同步中。
因为这些方法是用于操作线程状态的方法。
必须要明确到底操作的是哪个锁上的线程。
为什么操作线程的方法wait notify notifyAll定义在了Object类中?
因为这些方法是监视器的方法。监视器其实就是锁。
锁可以是任意的对象,任意的对象调用的方式一定定义在Object类中。
针对ThreadMsg1的补充 这里就时加入了等待唤醒机制,可以完成生成一个名字输出一个名字的效果
还有一个问题就是这里是一个生产者一个消费者类型
*/
/*共享资源*/
class Factory2{
public String name;
public String sex;
boolean flag=false;//false代表可以输出 true代表可以输出
}
/* 给factory赋值任务*/
class Input2 implements Runnable{
Factory2 f;
public Input2(Factory2 factory){
this.f=factory;
}
public void run() {
int num=0;
while(true){
synchronized (f) {
if (f.flag==false) {
if (num == 0) {
f.name = "zhanghongwei";
f.sex = "男男男男男";
} else {
f.name = "hwj";
f.sex = "女";
}
num = (num + 1) % 2;
f.flag=true;
f.notify();
}else{
try {
f.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
/* 给factory输出任务*/
class Output2 implements Runnable{
Factory2 f;
public Output2(Factory2 factory){
this.f=factory;
}
public void run() {
while (true) {
synchronized (f) {
if (f.flag) {
System.out.println("name:" + f.name + " sex:" + f.sex);
f.flag=false;
f.notify();
}else{
try {
f.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
/*主函数*/
public class ThreadMsg2 {
public static void main(String[] args) {
Factory2 factory =new Factory2();
Input2 in=new Input2(factory);
Output2 out =new Output2(factory);
Thread t1=new Thread(in);
Thread t2=new Thread(out);
t1.start();
t2.start();
}
}
3、引入多生产者多消费者问题
package com.hfview.thread;
/*
等待/唤醒机制。
针对多个生产者,多个消费者的解决
多生产者,多消费者的问题。
if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。
while判断标记,解决了线程获取执行权后,是否要运行!
notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。
notifyAll解决了本方线程一定会唤醒对方线程的问题。
*/
/*共享资源*/
class Factory3 {
private String name;
private int count = 1;
boolean flag = false;// false代表可以生产 true代表可以销售
/*
* 注意这种使用notify会造成死锁的原因 这也是notifyAll的应用
*/
public synchronized void make(String str) {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
name = str + count;
count++;
System.out.println(Thread.currentThread().getName() + ":制造了手机"
+ name);
flag = true;
this.notify();
}
}
/*
*如果 这种写法,那么一旦线程唤醒就会接着执行下面的语句
*而不是在从新判断flag的值,这样的写法一定切记不可写
*
*/
public synchronized void make2(String str) {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
name = str + count;
count++;
System.out.println(Thread.currentThread().getName() + ":制造了手机" + name);
flag = true;
this.notify();
}
public synchronized void show() {
if (flag) {
System.out.println(Thread.currentThread().getName() + ":销售了手机...."
+ name);
flag = false;
this.notify();
} else {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void show2() {
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":销售了手机...."
+ name);
flag = false;
this.notify();
}
}
/* 给factory赋值任务 */
class Input3 implements Runnable {
Factory3 f;
public Input3(Factory3 factory) {
this.f = factory;
}
public void run() {
while (true) {
f.make("inphone");
}
}
}
/* 给factory输出任务 */
class Output3 implements Runnable {
Factory3 f;
public Output3(Factory3 factory) {
this.f = factory;
}
public void run() {
while (true) {
f.show();
}
}
}
/* 主函数 */
public class ThreadMsg3 {
public static void main(String[] args) {
Factory3 factory = new Factory3();
Input3 in = new Input3(factory);
Output3 out = new Output3(factory);
Thread t1 = new Thread(in);
Thread t2 = new Thread(in);
Thread t3 = new Thread(out);
Thread t4 = new Thread(out);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
问题的出现:notifyAll()会唤醒这个锁上的所有线程,所以生产者线程不仅会唤醒消费者,也可能唤醒生成者线程,这就会浪费资源
4、1.5版本新特性解决上例不高效的问题
package com.hfview.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 利用jdk1.5新特性解决线程安全和通讯问题
* 这个例子是ThreadMsg3的另外一种写法,只不过更高效了
* @author zhw
*
*/
class Factory4{
private String name;
private int count;
private boolean flag=false;
final Lock lock =new ReentrantLock();
final Condition makeCon = lock.newCondition();//生产者线程锁里面的封装监视器的方法的对象
final Condition sellCon = lock.newCondition(); //消费者线程锁里面的封装监视器的方法的对象
public void make(String str){
try {
lock.lock(); //相当于syncsynchronized,这里就相当于获得一个锁
while(flag){
makeCon.await();//让当前的生产者线程阻塞
}
name=str+count;
System.out.println("生成了....."+name);
flag=true;
count++;
sellCon.signal();//让其他的一个消费者线程唤醒
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();//这里就是释放这个锁
}
}
public void sell(){
try {
lock.lock();
while(!flag){
sellCon.await();//让当前的消费者线程阻塞
}
System.out.println("销售了"+name);
flag=false;
makeCon.signal();//让其他的一个生产者线程唤醒
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
/* 给factory赋值任务 */
class Input4 implements Runnable {
Factory4 f;
public Input4(Factory4 factory) {
this.f = factory;
}
public void run() {
while (true) {
f.make("inphone");
}
}
}
/* 给factory输出任务 */
class Output4 implements Runnable {
Factory4 f;
public Output4(Factory4 factory) {
this.f = factory;
}
public void run() {
while (true) {
f.sell();
}
}
}
public class ThreadMsg4 {
public static void main(String[] args) {
Factory4 factory = new Factory4();
Input4 in = new Input4(factory);
Output4 out = new Output4(factory);
Thread t1 = new Thread(in);
Thread t2 = new Thread(in);
Thread t3 = new Thread(out);
Thread t4 = new Thread(out);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
当生产者线程满足条件进入到等待make.await()后,这个生产者线程就会相当于绑定到makeCon这个Conditon上,最后所有的生产者线程都会绑定到makeCon上面。同理所有的消费者线程就会绑定到sellCon这个Conditon上,当调用makeCon.signal()后就只会唤醒生成者线程,当调用sellCon.signal()后就只会唤醒消费者线程,这样就会高效了。
5、多生产者多消费者实例问题
前面几种都是生成一个东西,然后立马输出,这样根本不符合现实情况
package com.hfview.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 利用jdk1.5新特性解决线程安全和通讯问题
* 这个例子是针对ThreadMsg4的改进,前面几种都是生成一个 马上销售一个
* 但是现实中肯定不是这样的,这次是一次能生产10个
* @author zhw
*
*/
class Factory5{
/*加入一个手机厂商工厂只能生产10个手机*/
private String[] objArr = new String[10];
/*记录生产手机的编号*/
private int count=0;
/*库存量*/
private int num=0;
final Lock lock =new ReentrantLock();
final Condition makeCon = lock.newCondition();
final Condition sellCon = lock.newCondition();
public void make(String str){
try {
lock.lock();
//加入库存已满
while(num==objArr.length){
makeCon.await();
}
//库存不满
count++;
objArr[num]=str+count;
System.out.println(Thread.currentThread().getName()+"生产了...."+objArr[num]+" 当前数量:"+(num+1));
num++;
sellCon.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
public void sell(){
try {
lock.lock();
while(num==0){
sellCon.await();
}
System.out.println(Thread.currentThread().getName()+"销售了"+objArr[(num-1)]+" 当前数量:"+(num-1));
num--;
makeCon.signal();
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
/* 给factory赋值任务 */
class Input5 implements Runnable {
Factory5 f;
public Input5(Factory5 factory) {
this.f = factory;
}
public void run() {
while (true) {
f.make("inphone");
}
}
}
/* 给factory输出任务 */
class Output5 implements Runnable {
Factory5 f;
public Output5(Factory5 factory) {
this.f = factory;
}
public void run() {
while (true) {
f.sell();
}
}
}
public class ThreadMsg5 {
public static void main(String[] args) {
Factory5 factory = new Factory5();
Input5 in = new Input5(factory);
Output5 out = new Output5(factory);
Thread t1 = new Thread(in);
Thread t2 = new Thread(in);
Thread t3 = new Thread(out);
Thread t4 = new Thread(out);
t1.start();
t2.start();
t3.start();
t4.start();
}
}