您的位置:首页 > 编程语言 > Java开发

JAVA学习笔记Four:线程

2016-07-20 15:08 585 查看
     在JAVA中,程序、线程、进程的知识非常重要。程序是一组指令的集合,进程是正在运行的程序,线程是进程中的单一执行流。程序是静态的,二进程是动态的,一个进程对应一个程序,而一个程序可以有多个进程。进程有自己的独立运行空间,线程只能共享进程中的空间,一个进程中至少有一个线程(主线程main)。在JAVA编写中我们可以使用多线程使程序并发地执行。当程序的某个功能部分正在等待某些资源的时候,此时又不愿意因为等待而造成程序暂停,那么就可以创建另外的线程进行其它的工作。另外,多线程可以最大限度地减低CPU的闲置时间,从而提高CPU的利用率。

如何实现多线程?

//继承Thread类
public class MyThread extends Thread{
//重写run方法
public void run(){
//要做的事情
}
}


//实现Runnable接口
public class MyThread implements Runnable{
//实现run方法
public void run(){
//要做的事情
}
}

    我们人有生老病死,线程也有生命周期。



如上图,线程可以分为六种状态:

新建状态(new):使用new关键字创建线程对象,仅仅被分配了内存

就绪状态(ready):线程对象被创建后,等待它的start方法被调用,以获得CPU的使用权

运行状态(running):执行run方法,此时的线程的对象正占用CPU

睡眠状态(sleeping):调用sleep方法,线程被暂停,睡眠时间结束后,线程回到就绪状态,睡眠状态的线程不占用CPU

死亡状态(dead):run方法执行完毕后,线程进入死亡状态

阻塞状态(blocked):线程由于某些事件(如等待键盘输入)放弃CPU,暂停运行,直到线程重新进入就绪状态,才有机会转到运行状态

线程同步

线程同步有两个经典模型,分别是“监视线程”通讯模型和“生产者/消费者”模型。如果线程过多,为了更好管理线程,我们就可以建立一个监视线程运行的状态进行线程管理。

问题:多线程程序可能存在的一个问题就是:可能会出现多个线程同时操作某一个对象的数据,结果就会出现线程对象对自己操作的数据不同步的情况。

例如

package 银行取钱;

public class MyCount {
/**
* 银行账户类
*/
private int count;//定义账号金额
public String name;//定义账户名
//重写构造函数
public MyCount(String name,int count){
this.count=count;
this.name=name;
}
//定义取钱方法
public void getCash(int cash,String place){
if(count>=cash){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
count=count-cash;
System.out.println("取现地点为"+place+",取现金额为"+cash+"取现成功,余额为"+count);
}else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println("取现地点为"+place+",取现金额为"+cash+"取现失败,余额为"+count);
}

}
}


public class GetCashThread extends Thread{
/**
* 取钱线程
*/
private MyCount count;
private String place;//取钱地点
private int cash;
public GetCashThread(MyCount count,String place,int cash){
this.count=count;
this.place=place;
this.cash=cash;
}
//重写run方法
public void run() {
count.getCash(cash, place);
}

}


package 银行取钱;

public class Test {
/**
* 测试类
* @param args
*/
public static void main(String[] args) {
MyCount myCount=new MyCount("jyh",10000);
GetCashThread gct1=new GetCashThread(myCount, "ATW", 7000);
GetCashThread gct2=new GetCashThread(myCount, "柜台", 8000);
gct1.start();
gct2.start();
}

}


如上银行取钱程序,程序结果由于因为抢占资源的不确定性,结果不唯一,但都由于线程不同步,在判断时都判断count>cash,执行了取钱操作,使程序不能按理想方式运行,我们可以使用同步锁修改以上代码,避免程序发生通讯问题。

//修改取钱线程,锁住count
package 银行取钱;

public class GetCashThread extends Thread{
/**
* 取钱线程
*/
private MyCount count;
private String place;//取钱地点
private int cash;
public GetCashThread(MyCount count,String place,int cash){
this.count=count;
this.place=place;
this.cash=cash;
}
//重写run方法
public void run() {
synchronized(count){
count.getCash(cash, place);
}

}

}

分析synchronized方法:

synchronized代码块中的语句只能有一个线程在执行。任意一个对象都有一个标志位,有1和0两种状态,当程序执行到synchronized代码块的时候线程会检查对象的标志位是1还是0。如果是1则执行程序,同是将对象的标志位设置为0,其他线程执行到synchronized代码块时一看对象标志位为0 则线程会阻塞,一直等到对象的标志位为1再执行下面的程序 。

生产/消费模型

生产和消费线程共同操作一个集合,生产线程放入对象,消费线程取出对象!仅当集合中没有对象时,生产线程会放入一个对象,如有集合中有一个对象时,消费线程要马上取出这个对象。

wait/notify机制:

    在java中,每个对象都有从Object父类继承而来的两个关于线程间通讯的方法wait()和notify(),如其方法名所示,一个是等待,一个是通知,当在一个对象上调用wait()方法时,当前线程就会进行wait状态,直到收到另一个对象的notify()发出通知,才会执行下一步计算。在多线程通讯中,经常会用对象的这两个方法,一个典型的案例就是“生产/消费者模型”。

package 华为荣耀手机生产消费;

public class Honor {
/**
* Honor手机类
*/
private int number;
public Honor(int number){
this.number=number;
}
public int getNumebr(){
return number;
}
}


package 华为荣耀手机生产消费;

import java.util.ArrayList;

public class Factory extends Thread {
/**
* 工厂类
*/
public ArrayList<Honor> list;

public Factory(ArrayList<Honor> list) {
this.list = list;
}

public void run() {
int i = 1;
while (true) {
if (list.size() == 0) {

Honor honor = new Honor(i);
list.add(honor);
System.out.println("生产了一部Honor手机,序列号为" + i);
i++;
}else{
System.out.println("不能生产");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}

}

}


package 华为荣耀手机生产消费;

import java.util.ArrayList;

public class Customer extends Thread{
/**
* 消费类
*/
private ArrayList<Honor> list;
public Customer(ArrayList<Honor> list){
this.list=list;
}
@Override
public void run() {
while(true){
if(list.size()!=0){
Honor honor=list.get(0);
System.out.println("拿到一部Honor手机,序列号为"+honor.getNumebr());
list.remove(0);

}else{
System.out.println("没有拿到");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}

}

}


package 华为荣耀手机生产消费;

import java.util.ArrayList;

public class Test {
public static void main(String[] args) {
ArrayList<Honor> list=new ArrayList<Honor>();
Factory fact=new Factory(list);
Customer cus=new Customer(list);
fact.start();
cus.start();
}

}


代码如上,其中两个类在线程运行中会不停地查看队列,浪费大量资源,有时还会产生死锁,为了避免以上现象,加入wait()和notify()后Factory和Customer类更改为:

package 华为荣耀手机生产消费;

import java.util.ArrayList;

public class Factory extends Thread {
/**
* 工厂类
*/
public ArrayList<Honor> list;

public Factory(ArrayList<Honor> list) {
this.list = list;
}

public void run() {
int i = 1;
while (true) {
synchronized (list) {
if (list.size() == 0) {

Honor honor = new Honor(i);
list.add(honor);
System.out.println("生产了一部Honor手机,序列号为" + i);
i++;
} else {
System.out.println("不能生产");
try {
list.wait();
} catch (InterruptedException e1) {
// TODO 自动生成的 catch 块
e1.printStackTrace();
}
}

list.notify();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}

}

}

}


package 华为荣耀手机生产消费;

import java.util.ArrayList;

public class Customer extends Thread {
/**
* 消费类
*/
private ArrayList<Honor> list;

public Customer(ArrayList<Honor> list) {
this.list = list;
}

@Override
public void run() {
while (true) {
synchronized (list) {
if (list.size() != 0) {
Honor honor = list.get(0);
System.out.println("拿到一部Honor手机,序列号为" + honor.getNumebr());
list.remove(0);

} else {
System.out.println("没有拿到");
try {
list.wait();
} catch (InterruptedException e1) {
// TODO 自动生成的 catch 块
e1.printStackTrace();
}
}

list.notify();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}

}

}

}


注意:

 1)wait和notify必须在同步锁之内使用

  2)同步锁锁定对象和wait、notify对象必须同一个

  3)当对象wait挂起状态时候是会释放同步锁的

阻塞队列-生产消费模型

LinkedBlockingQueue阻塞队列,阻塞在存放数据时使用了“阻塞”的机制。  即当队列中有数据时,对阻塞队列的put()方法将用将会进入等待状态,直到队列中在空间放入数据;

   从阻塞队列中取数据时,如果阻塞队列中还没有数据,take()方法就会一直等待,直到队列被放入一个数据对象时,take()方法才会返回这个对象;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 线程