Java多线程13 妖的出现
需求:
资源有姓名和性别
两个线程
一个负责赋值/一个负责获取姓名和性别
要求1.运行代码,解决“妖”的问题
分析过程:典型的多线程安全问题
解决办法:加入同步,必须保证是同一个锁,解决妖的问题
要求二:实现数据的间隔输出
要求三:对代码进行重构。将name sex 私有化,资源类对外提供方法
要求四:将程序改为 Lock Condition 接口
代码:
package Thread;//妖的出现
//初始代码
//描述资源
class Resources{
String name;
String sex;
}
//线程任务————赋值
class Input implements Runnable{
private Resources r;
Input(Resources r){//线程任务一初始化就必须要有处理的资源
this.r=r;
}
public void run(){
int x = 0;
for(int y=0;y<5;y++){
if(x==0){
r.name ="Lizhuzhu";
r.sex = "男";
}
else{
r.name = "rose";
r.sex = "女";
}
x=(x+1)%2;//实现切换
}
}
}
//线程任务————获取
class Output implements Runnable{
private Resources r;
Output(Resources r){
this.r= r;
}
public void run(){
for(int y=0;y<5;y++){
System.out.println(r.name+"......"+r.sex);
}
}
}
public class YaoProblem {
public static void main(String[] args){
Resources r = new Resources();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
乍一看代码没有问题,但是实际运行结果就会出现数据错误
错误一:Lizhuzhu ..........女女
rose.............男
rose.............女女
rose.............女女
乍一看以为是多线程的安全问题(用同步解决)———直接在每个类中创建obj作为锁但是这sh
是不行的,当你在每个类中创建obj 的时候其实,你创建了两个不一样的obj 两个不一样的锁
程安全问题的前提1.有共享数据 2有多条语句操纵共享数据 3.也是最为重要的时,有多个线程共用同一个锁,多个线程共用一个锁才存在锁起来的问题。
因此要保证两个线程任务的锁是一样的:1.使用资源的对象(资源对象是唯一的)。2.使用类文件的class 文件,这个在内存中其实也是一个对象,而且是唯一
修改后的代码:
package Thread;//妖的出现
//初始代码
//描述资源
class Resources{
String name;
String sex;
}
//线程任务————赋值
class Input implements Runnable{
private Resources r;
Input(Resources r){//线程任务一初始化就必须要有处理的资源
this.r=r;
}
public void run(){
int x = 0;
for(int y=0;y<10;y++){
synchronized (r) {
if (x == 0) {
r.name = "Lizhuzhu";
r.sex = "男";
} else {
r.name = "rose";
r.sex = "女女";
}
}
x = (x + 1) % 2;//实现切换
}
}
}
//线程任务————获取
class Output implements Runnable{
private Resources r;
Output(Resources r){
this.r= r;
}
public void run(){
for(int y=0;y<10;y++){
synchronized (r) {
System.out.println(r.name + "......" + r.sex);
}
}
}
}
public class YaoProblem {
public static void main(String[] args){
Resources r = new Resources();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
但是这样修改后运行任然存在问题:数据结果呈现出只有一种结果(要么只有Lizhuzhu 男/要么只有rose 女女)
出现这种错误的原因:输出线程长时期的拥有CPU的执行权
问题:如何实现间隔输出:Lizhuzhu 男 rose 女女 Lizhuzhu 男 rose 女女
解决办法:使用等待唤醒机制————要设置标识
代码:
package Thread;//妖的出现
//初始代码
//描述资源
class Resources{
String name;
String sex;
boolean flag;
}
//线程任务————赋值
class Input implements Runnable{
private Resources r;
Input(Resources r){//线程任务一初始化就必须要有处理的资源
this.r=r;
}
public void run(){
int x = 0;
for(int y=0;y<10;y++) {
synchronized (r) {
if (r.flag) {
try {
r.wait();
} catch (InterruptedException e) {
}
}
if (x == 0) {
r.name = "Lizhuzhu";
r.sex = "男";
} else {
r.name = "rose";
r.sex = "女女";
}
r.flag=true;
r.notify();
}
x = (x + 1) % 2;//实现切换 }
}
}
}
//线程任务————获取
class Output implements Runnable{
private Resources r;
Output(Resources r){
this.r= r;
}
public void run(){
for(int y=0;y<10;y++){
synchronized (r) {
if(!r.flag){
try {
r.wait();
} catch (InterruptedException e) {
}
}
System.out.println(r.name + "......" + r.sex);
r.flag=false;
r.notify();
}
}
}
}
public class YaoProblem {
public static void main(String[] args){
Resources r = new Resources();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
注意代码细节!!!
对代码进行重构:
class Resources{
private String name;
private String sex;
boolean flag=false;
public synchronized void Input(String name,String sex){
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
}
}
this.name=name;
this.sex=sex;
flag=true;
this.notify();
}
public synchronized void Output(){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(name + "......" +sex);
flag=false;
this.notify();
}
}
class Input implements Runnable{
private Resources r;
Input(Resources r){
this.r=r;
}
public void run(){
int x=0;
for(int y=0;y<5;y++){
if(x==0){
r.Input("Lizhuzhu","男");
}
else{
r.Input("rose","女女");
}
x=(x+1)%2;
}
}
}
class Output implements Runnable{
private Resources r;
Output(Resources r){
this.r=r;
}
public void run(){
for(int y=0;y<5;y++) {
r.Output();
}
}
}
public class YaoProblem {
public static void main(String[] args){
Resources r = new Resources();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
需求四:使用Lock接口
代码:
class Resources{
private String name;
private String sex;
boolean flag=false;
private Lock lock = new ReentrantLock();
private Condition con = lock.newCondition();
public void Input(String name,String sex){
lock.lock();
try {
if (flag) {
try {
con.await();
} catch (InterruptedException e) {
}
}
this.name = name;
this.sex = sex;
flag = true;
con.signal();
}
finally {
lock.unlock();
}
}
public void Output(){
lock.lock();
try {
if (!flag) {
try {
con.await();
} catch (InterruptedException e) {
}
}
System.out.println(name + "......" + sex);
flag = false;
con.signal();
}
finally{
lock.unlock();
}
}
}
class Input implements Runnable{
private Resources r;
Input(Resources r){
this.r=r;
}
public void run(){
int x=0;
for(int y=0;y<5;y++){
if(x==0){
r.Input("Lizhuzhu","男");
}
else{
r.Input("rose","女女");
}
x=(x+1)%2;
}
}
}
class Output implements Runnable{
private Resources r;
Output(Resources r){
this.r=r;
}
public void run(){
for(int y=0;y<5;y++) {
r.Output();
}
}
}
public class YaoProblem {
public static void main(String[] args){
Resources r = new Resources();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}