1 线程协调

目的对各线程进行控制,保证各自执行的任务有条不紊且有序并行计算。尤其是在共享资源或者数据情况下。

1.1 易变volatile

cache技术虽然提高了访问数据的效率,但是有可能导致主存储器和cache中的值在某个瞬间的值不同。在多线程中,某个线程访问的可能是cache的值而非主存储器。

volatile保证线程直接访问主存储器,保证数据的一致性。volatile只能用于基本数据类型或者数组(boolean,byte, char, double ,float, integer, long, short),且不协调线程先后次序。

1.2 同步synchronized

定义某个程序块或者整个方法。协调多线程多某个方法或者程序块的访问。利用Monitor-lock技术,保证lock关关时,仅有一个线程使用某个对象。线程访问完毕,lock打开等待线程调度器分配给下一个线程。

【同步代码块】

          synchronized(同步对象){
              //需要同步的代码
           }
class hello implements Runnable {public void run() {for(int i=0;i<10;++i){synchronized (this) {//需要同步的对象if(count>0){try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(count--);}}}}public static void main(String[] args) {hello he=new hello();Thread h1=new Thread(he);Thread h2=new Thread(he);Thread h3=new Thread(he);h1.start();h2.start();h3.start();}private int count=5;
}

 【同步方法】

          synchronized 返回类型 方法名(参数列表){// 其他代码}
class hello implements Runnable {public void run() {for (int i = 0; i < 10; ++i) {sale();}}public synchronized void sale() {if (count > 0) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(count--);}}public static void main(String[] args) {hello he = new hello();Thread h1 = new Thread(he);Thread h2 = new Thread(he);Thread h3 = new Thread(he);h1.start();h2.start();h3.start();}private int count = 5;
}

1.3等待wait()与通知notify()及notifyAll()

wait()、notify()、notifyAll()是三个定义在Object类里的方法,用来控制线程的状态。

     ❶如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。可设定参数控制等待时长。如果没有指定参数,默认一直等待直到被通知。
             wait()
             wait(long)
             wait(long,int)

❷如果对象调用了notify方法就会随机通知某个正在等待这个对象的控制权的线程可以继续运行。

❸如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行

❹直接使用默认为当前对象。

1.4实例

实例1:使用2个进程交替输出“B<===>100  A<===>20”:

/*** 测试类**/
class test {public static void main(String[] args) {Info info = new Info();Producer pro = new Producer(info);Consumer con = new Consumer(info);new Thread(pro).start();new Thread(con).start();}
}class Info {private String name = "A";private int age = 20;private boolean flag=true;public String getName(){return name;}public int  getAge(){return age;}public synchronized void set(String name, int age){if(!flag)//一旦下面的代码执行过一次,则必须交出对象的使用权限try{wait();}catch (Exception e) {e.printStackTrace();}this.name=name;this.age=age;flag=false;notifyAll();}public synchronized void get(){if(flag)//一旦下面的代码执行过一次,则必须交出对象的使用权限try{wait();}catch (Exception e) {e.printStackTrace();}System.out.println(this.getName()+"<===>"+this.getAge());flag=true;notifyAll();}
}/*** 生产者* */
class Producer implements Runnable {private Info info = null;Producer(Info info) {this.info = info;}public void run() {boolean flag = false;for (int i = 0; i < 25; ++i) if (flag) {this.info.set("A", 20);flag = false;} else {this.info.set("B", 100);flag = true; }}
}/*** 消费者类***/
class Consumer implements Runnable {private Info info = null;public Consumer(Info info) {this.info = info;}public void run() {for (int i = 0; i < 25; ++i) {this.info.get();}}
}
      分析:
     ❶当输出成功时。使用notifyAll()使得等待的set()处于可执行态,调整flag。当下一轮输出循环时,flag保证输出函数的wait被激发,则等待的set()函数继续运行,重新设置数据。❷当设置成功时。使用notifyAll()使等待的get()处于可执行态,调整flag。当下一轮设置循环时,flag保证设置函数的set()被激发,则等待的get()函数继续进行,输出上一轮设置后的数据

实例2:

public class NotifyTest {  private String flag="true";  class NotifyThread extends Thread {  public NotifyThread(String name) {  super(name);  }  public void run() {try { System.out.println("notifyAll() begain");sleep(1000);} catch (InterruptedException e) {  e.printStackTrace(); }  synchronized (flag) {//对象flag的控制权,控制块 flag.notifyAll();//flag能够被其它进程控制 System.out.println("notifyAll() end");}  }
} class WaitThread extends Thread {  public WaitThread(String name) {  super(name);  }  public void run() {  synchronized (flag) {   System.out.println(getName() + " begin waiting!");  long waitTime = System.currentTimeMillis();try {flag.wait();} //放弃对象flag的控制权catch (InterruptedException e) {  e.printStackTrace(); }  waitTime = System.currentTimeMillis() - waitTime;  System.out.println(Thread.currentThread().getName() + " end waiting!,wait time :" + waitTime);  }  }
}public static void main(String[] args)  {  System.out.println("Main Thread Run!");  NotifyTest test = new NotifyTest();  NotifyThread notifyThread = test.new NotifyThread("notify01");  WaitThread waitThread01 = test.new WaitThread("waiter01");  WaitThread waitThread02 = test.new WaitThread("waiter02");  notifyThread.start();  waitThread01.start();  waitThread02.start();   }
} 

输出:

Main Thread Run!
notifyAll() begain
waiter02 begin waiting!
waiter01 begin waiting!
notifyAll() end
waiter01 end waiting!,wait time :1000
<pre style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.2000007629395px;">waiter02 end waiting!,wait time :1000
    注意:
         ❶任何一个时刻,对象的控制权(monitor)只能被一个线程拥有。
         ❷无论是执行对象的wait、notify还是notifyAll方法,必须保证当前运行的线程取得了该对象的控制权(monitor)
         ❸如果在没有控制权的线程里执行对象的以上三种方法,就会报java.lang.IllegalMonitorStateException异常。
         ❹JVM基于多线程,默认情况下不能保证运行时线程的时序。
         ❺进程的释放和控制都是与对象相关。

2.综合实例

2.1使用多个进程排序获得多维数组的最大值

public class FindMax
{public static void  main(String[] args){final int row=100,col=200;long startTime=0, endTime=0;double[][] matrix=randomMatrix.getMatrix(row, col);maxThread[] eachThread= new maxThread[row];double max=Double.MIN_VALUE;startTime=System.currentTimeMillis();for(int i=0;i<row;i++){eachThread[i]=new maxThread(matrix[i]);eachThread[i].start();}try{for(int i=0;i<row;i++){eachThread[i].join();//阻塞主进程直到当前进程运行完毕max=Math.max(max,  eachThread[i].getMax());}endTime=System.currentTimeMillis();}catch(InterruptedException e){System.out.println(e);}System.out.println("span="+(endTime-startTime)+"\nMax="+max);}
}/*****************生成随机数组************************/
class randomMatrix
{public static double[][] getMatrix(int row,int col){double[][]  matrix=new double[row][col];for(int i=0;i<row;i++)for(int j=0;j<col;j++)matrix[i][j]=Math.random()*100;return matrix;}
}/*****************排序进程************************/
class maxThread extends Thread
{private double max=Double.MIN_VALUE;private double[] data;maxThread(double[] array){data=array;}public double getMax(){ return max;}  public void run(){for(int i=0;i<data.length;i++)max=Math.max(max, data[i]);}
}

时间间隔为0。可见并行计算的效率极高。

2.2 生产-消费模拟

     设定场景
          1.多个生产者。当生产的数量大于特定数值时不再生产。
          2.多个消费者。当商店的物品不足一定数量时,不能消费。
          3.生产和消费都需要一定的时间。
          4.生产和消费进程都在同一个商店中

商品类

import java.text.NumberFormat;
public final class Product {private int ID;private double price;public Product(int num){ ID=num; price= Math.random()*20+5;}public String toString(){String amount=NumberFormat.getCurrencyInstance().format(price);return "ID: "+ID+";  Price"+amount;}
}

生产厂家:

public class producer extends Thread{private static volatile  int  productNumber;private Shop shop;public producer(Shop shop1){this.shop=shop1;}public void run(){try{productNumber++;Product product=new Product(productNumber);Thread.sleep((int)Math.random()*10000);//生产时间跨度shop.producting(product);//生产完毕,加入商店商品目录System.out.println(product+"product by " +this.getName());}catch(InterruptedException e){Thread.currentThread().interrupt();System.out.println(e);}}}

消费者:

public class consumer extends Thread{private Shop shop;public consumer(Shop shop1){this.shop=shop1;}public void run(){Product product;try{Thread.sleep((int) Math.random()*1000);//消费选择时间product=shop.consuming();//选择完毕,从商店货架移除System.out.println(product+"is consuming by "+ this.getName());}catch(InterruptedException e){Thread.currentThread().interrupt();}}}

商店:

package multithreadProduct;import java.util.*;
public class Shop {private volatile LinkedList<Product> productList=new LinkedList<Product>();// 产品队列public synchronized void producting(Product product)//线程协调方法{while(productList.size()>10){try{System.out.println("too much product, waiting consumers to buy......"+ productList.size());System.out.println("Producer: "+Thread.currentThread().getName()+" is  waiting....");wait(100);}catch(InterruptedException e){System.out.println(e);}}notifyAll();//通知其他线程有机会进入productList.addLast(product);System.out.println("Product success!  There are "+productList.size()+" products is avaliable");}public synchronized Product consuming()//线程协调方法{while(productList.size()<1){try{System.out.println("Product is not enough, only "+productList.size()+" is available");System.out.println("Consumer "+Thread.currentThread().getName()+" is waiting......");wait();}catch(InterruptedException e){System.out.println(e);}}Product product=productList.removeFirst();System.out.println("Consumeing Success! "+Thread.currentThread().getName()+" get what s/he want");return product;}public synchronized  int getSize()//线程协调方法{return productList.size();}
}<span style="font-size: 18px;"><strong>
</strong></span>
</pre><pre>

测试:

public class MultiThreadShop {public static void main(String[] args){final int numberOFproducer=100;//100个生产厂家final int numberOFconsumer=56;//56个消费者Thread[] producers=new producer[numberOFproducer];Thread[] consumers=new consumer[numberOFconsumer];Shop shop=new Shop();for(int i=0;i<producers.length;i++){producers[i]=new producer(shop);producers[i].start();}for(int i=0;i<consumers.length;i++){consumers[i]=new consumer(shop);consumers[i].start();}         }
}
  分析:
        ❶任何时候,只有一个进程可以进行产品的生产和消费操作。
        ❷产品数超过15。任何一个生产进程必须等待100毫秒,即等待消费。
        ❸产品数小于0。则无限等待。
        ❹进程协调的目标对象是shop,即任何时候对象shop的控制权只能被一个线程掌控。

3.其它知识点

       1.private static和public static的比较,区别在于修改的范围不同。但作用域是否全局,与具体线程无关。
       2.不同类型数组间赋值,抛出ArrayStoreException异常。
       3.有效利用多线程的关键是理解程序是并发执行而不是串行执行的。例如:程序中有两个子系统需要并发执行,这时候就需要利用多线程编程。
       4.通过对多线程的使用,可以编写出非常高效的程序。不过请注意,如果你创建太多的线程,程序执行的效率实际上是降低了,而不是提升了。
       5.请记住,上下文的切换开销也很重要,如果你创建了太多的线程,CPU花费在上下文的切换的时间将多于执行程序的时间!

参考

1.private static和public static的比较:多线程间

2. Java 多线程编程

3.最简实例说明wait、notify、notifyAll的使用方法

转载于:https://www.cnblogs.com/engineerLF/p/5393055.html

Java探索之旅(18)——多线程(2)相关推荐

  1. Java并发编程系列18:多线程之生产者和消费者模式_信号灯法(wait/notify通知机制)

    1.生产者消费者模式 生产者消费者问题(Producer-consumer problem),也称为有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例.该问题 ...

  2. Java探索之旅(11)——抽象类与接口

    1.Java数据类型 ❶不可变类,是指当创建了这个类的实例后,就不允许修改它的属性值. 它包括: Primitive变量:boolean,byte, char, double ,float, inte ...

  3. Java探索之旅(6)——对象和类

    1.知识要点 假设: public ClassName{int data;String name;ClassName(){data=1;}public static int getdata(){ret ...

  4. Java探索之旅(16)——异常处理

    1.异常与异常处理 在<java编程思想>中这样定义 异常:阻止当前方法或作用域继续执行的问题.虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来 ...

  5. Java探索之旅(2)——GUI输入输出与代码的规范性

    1.知识点概叙 ① 定名常量:关键字final,类似C++ const定义,一般用大写:final double PI=3.1415926 ② 5/2=2:5.0/2=2.5://通常意义的除法,至少 ...

  6. 【C++探索之旅】开宗明义+第一部分第一课:什么是C++?

    内容简介 1.课程大纲 2.第一部分第一课:什么是C++? 3.第一部分第二课预告:C++编程的必要软件 开宗明义 亲爱的读者,您是否对C++感兴趣,但是C++看起来很难,或者别人对你说C++挺难的, ...

  7. C++探索之旅 | 第一部分第一课:什么是C++

    作者 谢恩铭,公众号「程序员联盟」(微信号:coderhub). 转载请注明出处. 原文:https://www.jianshu.com/p/a8c3a44f57ef <C++探索之旅>全 ...

  8. Android探索之旅 | 面向对象和Java基础

    -- 作者 谢恩铭 转载请注明出处 上一篇 Android探索之旅 | Android简介 中说到: "Android的默认开发语言是Java,入门简单.而且,你的Java水平不需要多好就可 ...

  9. command对象提供的3个execute方法是_前阿里P9的Java面试重点3:多线程

    1. 并行和并发有什么区别? 并行:多个处理器或多核处理器同时处理多个任务. 并发:多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行. 如下图: 并发 = ...

  10. Web 探索之旅 | 第二部分第一课:客户端语言

    -- 作者 谢恩铭 转载请注明出处 Web探索之旅 | 第二部分 第二部分第一课:客户端语言 第二部分第二课:服务器语言 第二部分第三课:框架和内容管理系统 第二部分第四课:数据库 第二部分第五课:响 ...

最新文章

  1. XML解析简介及Xerces-C++简单使用举例
  2. iOS开发网络数据之AFNetworking使用
  3. 您的凭依不工作/登录没有成功
  4. python load_Python实例:numpy.load()的使用
  5. 人口问题,怎样的生育率才能保持正常的世代更替?
  6. linux输入qsub显示错误,linux – 使用qsub运行shellscript的’意外的文件结束’和’错误导入功能定义’错误...
  7. Python——集合字典解析的对象无序和无副本特性
  8. 修改pom文件_自动化测试基础篇:Selenium 框架设计(POM)
  9. 介绍一个工具给大家,做网站时,经常要上传文件到外网服务器,但是上传时往往需要很长时间,如果有一个文件对比工具……...
  10. Linux内核中的atoi,itoa等函数
  11. libcurl的封装,支持同步异步请求,支持多线程下载,支持https
  12. 计算机远程用户关机指令,有哪些远程关机命令?如何用向日葵进行远程关机电脑?...
  13. progress进度条滚动动画
  14. 这个Q版卡通头像生成器,太符合程序员形象了
  15. 星级评价,点击星星进行评分
  16. 看JTS源码,感受Java优化编程
  17. 统计二叉树的叶子节点个数
  18. Open3D点云库 C++学习笔记
  19. 2022年双十二有哪些tws蓝牙耳机?值得入手的tws耳机推荐
  20. 看雪学院-解密入门教学(二)笔记

热门文章

  1. 14套新鲜出炉的网页图标素材下载
  2. 美国人竟然是这样教育小学
  3. k8s Custom Resource
  4. 《大型网站技术架构:核心原理与案例分析》阅读笔记四
  5. c语言范式编程之lsearch
  6. 中国农历2013,2014 (zz.IS2120@BG57IV3)
  7. 用ASP.NET刷新页面的几种有效方法
  8. flume学习(四):Flume Channel Selectors使用
  9. Java并发编程之原子类
  10. 集算器(仓库版)发布,黑科技获得用户好评