JAVA学习笔记Four:线程
2016-07-20 15:08
585 查看
在JAVA中,程序、线程、进程的知识非常重要。程序是一组指令的集合,进程是正在运行的程序,线程是进程中的单一执行流。程序是静态的,二进程是动态的,一个进程对应一个程序,而一个程序可以有多个进程。进程有自己的独立运行空间,线程只能共享进程中的空间,一个进程中至少有一个线程(主线程main)。在JAVA编写中我们可以使用多线程使程序并发地执行。当程序的某个功能部分正在等待某些资源的时候,此时又不愿意因为等待而造成程序暂停,那么就可以创建另外的线程进行其它的工作。另外,多线程可以最大限度地减低CPU的闲置时间,从而提高CPU的利用率。
如何实现多线程?
我们人有生老病死,线程也有生命周期。
如上图,线程可以分为六种状态:
新建状态(new):使用new关键字创建线程对象,仅仅被分配了内存
就绪状态(ready):线程对象被创建后,等待它的start方法被调用,以获得CPU的使用权
运行状态(running):执行run方法,此时的线程的对象正占用CPU
睡眠状态(sleeping):调用sleep方法,线程被暂停,睡眠时间结束后,线程回到就绪状态,睡眠状态的线程不占用CPU
死亡状态(dead):run方法执行完毕后,线程进入死亡状态
阻塞状态(blocked):线程由于某些事件(如等待键盘输入)放弃CPU,暂停运行,直到线程重新进入就绪状态,才有机会转到运行状态
线程同步
线程同步有两个经典模型,分别是“监视线程”通讯模型和“生产者/消费者”模型。如果线程过多,为了更好管理线程,我们就可以建立一个监视线程运行的状态进行线程管理。
问题:多线程程序可能存在的一个问题就是:可能会出现多个线程同时操作某一个对象的数据,结果就会出现线程对象对自己操作的数据不同步的情况。
例如
如上银行取钱程序,程序结果由于因为抢占资源的不确定性,结果不唯一,但都由于线程不同步,在判断时都判断count>cash,执行了取钱操作,使程序不能按理想方式运行,我们可以使用同步锁修改以上代码,避免程序发生通讯问题。
分析synchronized方法:
synchronized代码块中的语句只能有一个线程在执行。任意一个对象都有一个标志位,有1和0两种状态,当程序执行到synchronized代码块的时候线程会检查对象的标志位是1还是0。如果是1则执行程序,同是将对象的标志位设置为0,其他线程执行到synchronized代码块时一看对象标志位为0 则线程会阻塞,一直等到对象的标志位为1再执行下面的程序 。
生产/消费模型
生产和消费线程共同操作一个集合,生产线程放入对象,消费线程取出对象!仅当集合中没有对象时,生产线程会放入一个对象,如有集合中有一个对象时,消费线程要马上取出这个对象。
wait/notify机制:
在java中,每个对象都有从Object父类继承而来的两个关于线程间通讯的方法wait()和notify(),如其方法名所示,一个是等待,一个是通知,当在一个对象上调用wait()方法时,当前线程就会进行wait状态,直到收到另一个对象的notify()发出通知,才会执行下一步计算。在多线程通讯中,经常会用对象的这两个方法,一个典型的案例就是“生产/消费者模型”。
代码如上,其中两个类在线程运行中会不停地查看队列,浪费大量资源,有时还会产生死锁,为了避免以上现象,加入wait()和notify()后Factory和Customer类更改为:
注意:
1)wait和notify必须在同步锁之内使用
2)同步锁锁定对象和wait、notify对象必须同一个
3)当对象wait挂起状态时候是会释放同步锁的
阻塞队列-生产消费模型
LinkedBlockingQueue阻塞队列,阻塞在存放数据时使用了“阻塞”的机制。 即当队列中有数据时,对阻塞队列的put()方法将用将会进入等待状态,直到队列中在空间放入数据;
从阻塞队列中取数据时,如果阻塞队列中还没有数据,take()方法就会一直等待,直到队列被放入一个数据对象时,take()方法才会返回这个对象;
如何实现多线程?
//继承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对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树