1、问题描述
package thread;
//任务:给资源赋值,赋值之后还要取出来。赋值和取出本身就可以同时执行。
//描述资源
class Resource{
String name;
String sex;
}
//赋值线程任务
class Input implements Runnable{
//线程一开始就要有需要处理的资源
//输入和输出处理的是同一个资源,所以不用new在里面,而是传递进来就可以了。
private Resource r;
//构造方法
Input(Resource r){
this.r = r;
}
//任务代码
public void run(){
int x = 0;
while (true) {
if(x == 0){
r.name = "张飞" ;
r.sex = "男";
}else {
r.name = "rose";
r.sex = "女";
}
x = (x+1)%2;
}
}
}
class Output implements Runnable{
private Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while (true){
System.out.println(r.name+"..."+r.sex);
}
}
}
public class yao_practice {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
会发现有时候张飞会变成女的,rose会变成男的。
原因:多线程的安全问题。
加了同步之后,也没有解决问题
package thread;
//任务:给资源赋值,赋值之后还要取出来。赋值和取出本身就可以同时执行。
//描述资源
class Resource{
String name;
String sex;
}
//赋值线程任务
class Input implements Runnable{
//线程一开始就要有需要处理的资源
//输入和输出处理的是同一个资源,所以不用new在里面,而是传递进来就可以了。
private Resource r;
private Object obj = new Object();
//构造方法
Input(Resource r){
this.r = r;
}
//任务代码
public void run(){
int x = 0;
while (true) {
synchronized (obj){
if(x == 0){
r.name = "张飞" ;
r.sex = "男";
}else {
r.name = "rose";
r.sex = "女";
}
x = (x+1)%2;
}
}
}
}
class Output implements Runnable{
private Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while (true){
System.out.println(r.name+"..."+r.sex);
}
}
}
public class yao_practice {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
为什么不行?同步的前提是 必须是多线程用同一个锁。
解决问题:
package thread;
//任务:给资源赋值,赋值之后还要取出来。赋值和取出本身就可以同时执行。
//描述资源
class Resource{
String name;
String sex;
}
//赋值线程任务
class Input implements Runnable{
//线程一开始就要有需要处理的资源
//输入和输出处理的是同一个资源,所以不用new在里面,而是传递进来就可以了。
private Resource r;
private Object obj = new Object();
//构造方法
Input(Resource r){
this.r = r;
}
//任务代码
public void run(){
int x = 0;
while (true) {
synchronized (r){
if(x == 0){
r.name = "张飞" ;
r.sex = "男";
}else {
r.name = "rose";
r.sex = "女";
}
x = (x+1)%2;
}
}
}
}
class Output implements Runnable{
private Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while (true) {
synchronized (r) {
System.out.println(r.name + "..." + r.sex);
}
}
}
}
public class yao_practice {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
结果是,要是张飞男,则一直输出张飞男。若是rose女,则一直输出rose女。
解决:使用等待唤醒机制。wait,notify。
package thread;
//任务:给资源赋值,赋值之后还要取出来。赋值和取出本身就可以同时执行。
//描述资源
class Resource{
String name;
String sex;
boolean flag = false;
}
//赋值线程任务
class Input implements Runnable{
//线程一开始就要有需要处理的资源
//输入和输出处理的是同一个资源,所以不用new在里面,而是传递进来就可以了。
private Resource r;
private Object obj = new Object();
//构造方法
Input(Resource r){
this.r = r;
}
//任务代码
public void run(){
int x = 0;
while (true) {
synchronized (Resource.class){
if(r.flag){
try {
Resource.class.wait(); //必须要标识锁,才能知道哪个锁可以唤醒它。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x == 0){
r.name = "张飞" ;
r.sex = "男";
}else {
r.name = "rose";
r.sex = "女";
}
r.flag = true;
Resource.class.notify();
x = (x+1)%2;
}
}
}
}
class Output implements Runnable{
private Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while (true) {
synchronized (Resource.class) {
if(!r.flag){
try {
Resource.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(r.name + "..." + r.sex);
r.flag = false;
Resource.class.notify();
}
}
}
}
public class yao_practice {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
2、代码重构
把不该有的去掉。
package thread;
//任务:给资源赋值,赋值之后还要取出来。赋值和取出本身就可以同时执行。
//描述资源
class Resource{
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name,String sex) throws InterruptedException {
if(flag){
this.wait();
}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out() {
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+" "+sex);
flag = false;
this.notify();
}
}
//赋值线程任务
class Input implements Runnable{
//线程一开始就要有需要处理的资源
//输入和输出处理的是同一个资源,所以不用new在里面,而是传递进来就可以了。
private Resource r;
private Object obj = new Object();
//构造方法
Input(Resource r){
this.r = r;
}
//任务代码
public void run(){
int x = 0;
while (true) {
if(x == 0){
try {
r.set("zhangfei","nan");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
try {
r.set("rose","lady");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
x = (x+1)%2;
}
}
}
class Output implements Runnable{
private Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while (true) {
r.out();
}
}
}
public class yao_practice {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
封装完之后,直接使用方法即可。
3、升级到lock方式
lock代替同步函数或者同步代码块
condition代替监视器方法,将监视器方法从锁上分离出来单独封装到condition对象中。
package thread.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Resource{
private String name;
private String sex;
private boolean flag = false;
private final Lock lock = new ReentrantLock();
private Condition con = lock.newCondition();
//method:赋值
public void set(String name,String sex){
lock.lock();
try{
if(flag){
try {
con.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.sex = sex;
flag = true;
con.signal();
}finally {
lock.unlock();
}
}
//method:获取值
public void out(){
lock.lock();
try {
if(!flag){
try {
con.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+" "+sex);
flag = false;
con.signal();
}finally {
lock.unlock();
}
}
}
//赋值线程任务
class Input implements Runnable{
private Resource r;
Input(Resource r){
this.r = r;
}
public void run(){
int x = 0;
while (true){
if(x == 0){
r.set("张飞","男");
}
else {
r.set("rose","nv");
}
x = (x+1)%2;
}
}
}
//获取的线程任务
class Output implements Runnable{
Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while (true){
r.out();
}
}
}
public class lock_test {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}