java中的线程
来源:互联网 发布:照片素描软件 编辑:程序博客网 时间:2024/06/05 21:57
线程同步问题:
在运行多线程程序时,有可能在运行多次之后出现不合理的结果,例如银行提款系统。若夫妻两人用同一个账户在银行存了5000元,某一天丈夫和妻子都去取钱,而且双方不知道,丈夫用银行卡从ATM机取,妻子用存折在银行柜台处取,两人同时取现的情况下,可能会出现两人取现总金额大于5000的情况(当然只是例子,银行不可能会出现这种情况)。
这种情况出现的原因就是没有处理线程同步的问题。
package Thread;
public class Account {
public int count;
public Account(int count){
this.count =count;
}
public int getCash(int cash){if(cash>count){
//System.out.println("余额不够,请重新取现");
return -1;
}
//模拟柜台人员或者ATM机数钱过程
try {
Thread.sleep(1000);
} catch (InterruptedExceptione) {
e.printStackTrace();
}
count =count-cash;
return count;
}
}
例如上面这段代码,写的是一个账户类,代表着夫妻二人的账户,cash表示账户余额,getCash()方法表示从账户中提取现金。若提取金额合理(提取金额少于账户余额),则返回现在的账户余额,若金额不合理,则返回-1,表示提取失败。
package Thread;
public class Peopleextends Thread{
public String type;
public Account account;
public int cash;
public People(Accountaccount,String type,int cash){
this.account=account;
this.cash=cash;
this.type=type;
}
public void run(){
excute();
}
public void excute(){
int result =account.getCash(cash);
if(result<0){
System.out.println("银行余额不足。取现方式为"+type+",取现失败,取现额度为:"+cash);
}
else{
System.out.println("取现方式为"+type+",取现成功,取现额度为:"+cash+",银行余额为:"+account.count);
}
}
}
上面代码表示个人类并继承Thread类,成为一个线程,并对其重写run()方法。type表示取款方式(暂定ATM和柜台取款),account表示此类所拥有的账户,cash表示此类所要提取的现金。Excute()方法根据账户类的getCash()方法返回的值判断是否取现成功。若成功则打印取现方式为:___,取现成功,取现额度为:___,银行余额为:___,反之,则打印不成功并输出账户余额。
package Thread;
public class Test {
public static void main(String[]args){
Account account = new Account(5000);
People people1 = new People(account,"ATM",4000);
People people2 = new People(account,"存折",3000);
people1.start();
people2.start();
}
}
上面代码表示程序的入口。建立一个账户类account,存入5000元,并新建两个个人类people1和people2。两人共用一个账户。启动两个个人类线程,在运行若干次后可发现,程序运行出了违反逻辑的结果:
银行余额不足。取现方式为ATM,取现失败,取现额度为:4000
取现方式为存折,取现成功,取现额度为:3000,银行余额为:-2000
或
取现方式为存折,取现成功,取现额度为:3000,银行余额为:2000
取现方式为ATM,取现成功,取现额度为:4000,银行余额为:2000
不管哪种结果,显然都不是我们想要的,这种结果的出现就是没有处理线程同步的结果。若要得到正常结果,只要在出现线程同步的地方加上synchronized。synchronized有两种用法,一种是加在方法的方法名称上,例如public synchronized int getCash(int cash){}
还有一种就是加在方法体中
public synchronized int getCash(int cash){
synchronized(this){
if(cash>count){
//System.out.println("余额不够,请重新取现");
return -1;
}
//模拟柜台人员或者ATM机数钱过程
try {
Thread.sleep(1000);
} catch (InterruptedExceptione) {
e.printStackTrace();
}
}
count =count-cash;
return count;
}
synchronized后面的括号中表示的是发生线程同步的对象。因为Account类中的count属性被两个People类共用。
接下来体验下wait()和notify()方法。
在java中,每个对象都有从Object父类继承而来的两个关于线程间通讯的方法wait()和notify(),如其方法名所示,一个是等待,一个是通知,当在一个对象上调用wait()方法时,当前线程就会进行wait状态,直到收到另一个对象的notify()发出通知,才会执行下一步计算。
注意:在java中wait和notify必须在同步锁之内使用。
同步锁锁定对象和wait、notify对象必须同一个。
当对象wait挂起状态时候是会释放同步锁的。
首先,写一个消费者和生产者之间关系的代码。新建一个ArrayList类当做仓库,产品为Phone类,消费者Customer和生产者Producter。若仓库里有手机,则被消费者消费,若无,则生产者生产一部手机放入仓库。也就是说仓库中最多只能有一部手机。
package com.huaxin.Customer;
public class Phone {
public String name;
public Phone(String name){
this.name=name;
}
}
Phone类,。只有一个name属性,由构造方法传入
package com.huaxin.Customer;
import java.util.ArrayList;
public class Customerextends Thread{
public ArrayList<Phone>list;
public Customer(ArrayList<Phone>list){
this.list =list;
}
public void run(){
while(true){
synchronized (list) {
//检查容器中是否还有产品,若没有产品,则等待并继续检测
if(list.size()==0){
try {
list.wait();
} catch (InterruptedExceptione) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//否则从容器中拿出一个产品
else{
Phone phone = list.remove(0);
System.out.println("消费者消费了一个手机,型号是:"+phone.name);
list.notify();
}
}
}
}
}
Customer类继承Thread,有一个代表仓库的ArrayList属性,由构造方法传入。然后重写线程的run()方法,在run()方法中对仓库的库存进行判断,在这里用synchronized (list)处理线程同步(产生原因是list被消费者和生产者共用)。利用while(true)判断,若库存为1,则消费一部Phone,并立即告诉生产者Producter(使用notify()方法)。若库存为0,则等待(使用wait()方法)。
package com.huaxin.Customer;
import java.util.ArrayList;
public class Producterextends Thread{
public ArrayList<Phone>list;
public Producter(ArrayList<Phone>list) {
this.list=list;
}
@Override
public void run() {
int index = 1;
//检测容器list,若有产品则不生产
while(true){
synchronized (list) {
if(list.size()>0){
//有产品,等待产品被消费者消费
try {
list.wait();
} catch (InterruptedExceptione) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else{
//没有产品,则生产一个
Phone phone = new Phone(index+"号手机");
list.add(phone);
index++;
System.out.println("生产者生产了一个手机,型号是:"+phone.name);
//通知消费者消费产品
list.notify();
}
}
}
}
}
Producter类和Customer类一个到理,只是判断条件不同。
package com.huaxin.Customer;
import java.util.ArrayList;
public class Test {
public static void main(String[]args) {
ArrayList<Phone> list = new ArrayList<Phone>();
//生产者线程
Producter pro1 = new Producter(list);
//消费者线程
Customer cus1 = new Customer(list);
Customer cus2 = new Customer(list);
Customer cus3 = new Customer(list);
//启动线程
pro1.start();
cus1.start();
cus2.start();
cus3.start();
}
}
最后程序入口,利用一个生产者三个消费者共四个线程,运行程序得出结果
生产者生产了一个手机,型号是:1号手机
消费者消费了一个手机,型号是:1号手机
生产者生产了一个手机,型号是:2号手机
消费者消费了一个手机,型号是:2号手机
由此得到:在线程wait状态时,线程会一直阻塞我,直到notify的到来,而notify会使线程的wait状态变为就绪状态。
进程的状态分为:新建状态、就绪状态、运行状态、睡眠状态、死亡状态、阻塞状态,其中睡眠状态和阻塞状态执行完毕(睡眠状态的睡眠时间完成,阻塞状态得到notify)之后会变味就绪状态。
线程池
首先,线程使用时需要创建,运行完之后又会进入死亡状态,再次需要时仍要创建,但线程池的存在使得线程会一直存在,一直等待任务的到来,不会消失,所以使用时只需创建一次,不必创建很多个线程,从而减小了运行内存的占用。
package com.huaxin.pool;
import java.util.LinkedList;
public class MyPool {
//数组存储工人
public Worker[]workers;
//ArrayList存储任务
public LinkedList<MyTask> list =new LinkedList<MyTask>() ;
//构造方法
public MyPool(int size) {
workers = new Worker[size];
//定义工人
for(int i=0;i<workers.length;i++){
Worker worker =new Worker(i+"号工人",list);
worker.start();
workers[i] =worker;
}
}
//增加任务(线程)
public void getTask(MyTasktask){
synchronized (list){
//增加任务
list.add(task);
//通知给工人
list.notify();
}
}
}
首先,上述代码创建了一个线程池类MyPool,里面有两个容器,数组用来存放Worker类,表示工人;LinkedList用来存放MyTask类,表示任务。getTask()方法表示向LinkedList中增加任务。
package com.huaxin.pool;
public class MyTaskimplements Runnable{
public String name;
public MyTask(Stringname){
this.name =name;
}
public void work(){
System.out.println(name+"开始执行");
try {
Thread.sleep(3000);
} catch (InterruptedExceptione) {
e.printStackTrace();
}
System.out.println(name+"任务执行完毕");
}
public void run() {
work();
}
}
MyTask类实现了Runnable接口,并重写了其run()方法,表示任务。
package com.huaxin.pool;
import java.util.LinkedList;
public class Workerextends Thread{
public String name;
//队列
public LinkedList<MyTask>list;
public Worker(Stringname, LinkedList<MyTask> list) {
this.name =name;
this.list =list;
}
//重写run方法、
public void run(){
//对任务池进行判断
while(true){
MyTask task;
synchronized (list) {
//任务池为空,则线程进入阻塞状态
if(list.size()==0){
try {
list.wait();
} catch (InterruptedExceptione) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//else{
//若任务池不为空,则线程进入就绪状态并执行任务
task = list.removeFirst();
//}
}
System.out.println(this.name+"正在执行:"+task.name);
task.work();
System.out.println(this.name+"执行完毕:"+task.name);
}
}
}
Worker类,继承了Thread类,并重写了run方法,在run方法里对LinkedList类的对象list进行判断,若list中没有任务,则工人等待(wait()方法),若list中有任务,则取出来并执行。
package com.huaxin.pool;
public class Test {
public static void main(String[]args) {
//10个工人
MyPool pool = new MyPool(5);
//20个任务
for (int i = 0;i < 10; i++) {
MyTask task = new MyTask(i+"号任务");
pool.getTask(task);
}
}
}
程序入口,再次创建了一个包含5个工人的线程池并新建10个任务供工人们工作。
运行程序得到结果:
1号工人正在执行:1号任务
2号工人正在执行:2号任务
3号工人正在执行:3号任务
0号工人正在执行:0号任务
0号任务开始执行
3号任务开始执行
2号任务开始执行
1号任务开始执行
4号工人正在执行:4号任务
4号任务开始执行
2号任务任务执行完毕
4号任务任务执行完毕
1号任务任务执行完毕
2号工人执行完毕:2号任务
3号任务任务执行完毕
3号工人执行完毕:3号任务
0号任务任务执行完毕
3号工人正在执行:6号任务
6号任务开始执行
2号工人正在执行:5号任务
4号工人执行完毕:4号任务
1号工人执行完毕:1号任务
4号工人正在执行:7号任务
5号任务开始执行
0号工人执行完毕:0号任务
7号任务开始执行
1号工人正在执行:8号任务
0号工人正在执行:9号任务
9号任务开始执行
8号任务开始执行
6号任务任务执行完毕
5号任务任务执行完毕
3号工人执行完毕:6号任务
7号任务任务执行完毕
4号工人执行完毕:7号任务
9号任务任务执行完毕
8号任务任务执行完毕
1号工人执行完毕:8号任务
0号工人执行完毕:9号任务
2号工人执行完毕:5号任务
由结果看,线程池中的线程在运行完分配的任务后并没有死亡,而是进入阻塞状态,若有任务就会进入就绪状态执行任务。
- Java中的线程
- Java中的线程续
- Java中的线程同步
- JAVA中的线程池
- java中的线程池
- Java中的线程池
- java中的线程池
- java中的线程
- Java中的线程
- Java SE中的线程
- java中的线程同步
- java中的线程
- java线程中的join
- java中的线程
- JAVA中的守护线程
- java中的线程池
- Java中的线程池
- Java中的线程
- linux和mac/windows文件传输
- ubuntu查看已安装所有软件包
- 动态时间
- 随机数
- git命令自动补全
- java中的线程
- 谷歌电子市场开发流程(5)-关于加载更多页面的处理
- git与svn的五个基本区别[转]
- 快速创建React Native App
- jQuery自定义下拉框
- 漫画算法B树
- 有用的网站
- xgboost与gbdt
- 每次在windows上看到有流量在后台跑我都会...