public class Client {
public Data request(final String queryStr){
final FutureData future=new FutureData();
new Thread(){
public void run(){
RealData realData=new RealData(queryStr);
future.setRealData(realData);
}
}.start();
return future;
}
}
public interface Data {
String getResult();
}
public class FutureData implements Data {
//FutureData是RealData的包裝
protected RealData realData=null;
protected boolean isReady=false;
public synchronized void setRealData(RealData realData){
if(isReady)
return;
this.realData=realData;
isReady=true;
notifyAll();
}
@Override
public synchronized String getResult() {
while (!isReady){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return realData.result;
}
}
public class RealData implements Data {
protected final String result;
public RealData(String para){
//RealData的構造很慢
StringBuffer sb=new StringBuffer();
for (int i=0;i<10;i++){
sb.append(para);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
result=sb.toString();
}
@Override
public String getResult() {
return result;
}
}
Master-Worker 的好處是可以將大任務分為若干個小任務,並行執行。
public class Master {
//任務佇列
protected Queue<Object> workQueue=new ConcurrentLinkedQueue<>();
//Woker執行緒佇列
protected Map<String,Thread> threadMap=new HashMap<>();
//子任務的結果集
protected Map<String,Object> resultMap=new ConcurrentHashMap<>();
//是否所有子任務都結束了
public boolean isComplete(){
for (Map.Entry<String,Thread> entry:threadMap.entrySet()){
if(entry.getValue().getState()!=Thread.State.TERMINATED){
return false;
}
}
return true;
}
public Master(Worker worker,int countWorker){
worker.setWorkQueue(workQueue);
worker.setResultMap(resultMap);
for (int i = 0; i <countWorker ; i++) {
threadMap.put(Integer.toString(i),new Thread(worker,Integer.toString(i)));
}
}
//提交一個任務
public void submit(Object object){
workQueue.add(object);
}
//返回子任務結果集
public Map<String,Object> getResultMap(){
return resultMap;
}
//開始執行所有的Worker程序
public void execute(){
for (Map.Entry<String,Thread> entry:threadMap.entrySet()){
entry.getValue().start();
}
}
}
public class Worker implements Runnable {
//任務佇列,用於取得子任務
protected Queue<Object> workQueue;
//子任務處理結果集
protected Map<String,Object> resultMap;
public void setWorkQueue(Queue<Object> workQueue) {
this.workQueue = workQueue;
}
//子任務的處理邏輯,在子類中具體實現
public Object handle(Object input){
return input;
}
public void setResultMap(Map<String, Object> resultMap) {
this.resultMap = resultMap;
}
@Override
public void run() {
while (true){
Object input=workQueue.poll();
if(input==null)break;
Object result=handle(input);
resultMap.put(Integer.toString(input.hashCode()),result);
}
}
}
public class PlusWorker extends Worker {
@Override
public Object handle(Object input) {
if(input instanceof Integer){
Integer i=(Integer)input;
return i*i*i;
}
return 0;
}
}
測試程式碼:
@Test
public void test26(){
long start=System.currentTimeMillis();
Master master=new Master(new PlusWorker(),5);
for (int i = 0; i <100 ; i++) {
master.submit(i);
}
master.execute();
long re=0;
Map<String,Object> resultMap=master.getResultMap();
while (resultMap.size()>0||!master.isComplete()){
Set<String> keys = resultMap.keySet();
String key=null;
for (String k:keys){
key=k;
break;
}
Integer i=null;
if(key!=null)
i=(Integer)resultMap.get(key);
if(i!=null)
re+=i;
if(key!=null)
resultMap.remove(key);
}
System.out.println(re);
System.out.println(System.currentTimeMillis()-start);
}
Ncpu:CPU 的數量
Ucpu:目標 Cpu 的使用率 0<=Ucpu<=1
W/C:等待時間和計算時間的比率
最優執行緒池大小為:
Nthreads=NcpuUcpu(1+W/C);
ThreadPoolExecutor 是一個可以擴充套件的執行緒池,它提供了 beforeExecutor()和 afterExecutor()和 terminated()3 個方法進行擴充套件。
public class LockTest {
private static final int CIRCLE=20000000;
public static void main(String[] args) {
long start=System.currentTimeMillis();
for (int i = 0; i <CIRCLE ; i++) {
createStringBuffer("java","Performance");
}
long bufferCost=System.currentTimeMillis()-start;
System.out.println("CreateStringBuffer:"+bufferCost+"ms");
}
public static String createStringBuffer(String s1,String s2){
StringBuffer sb=new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
}
}
/**
* Amino框架實現了Master-woker模式
*/
public class Pow3 implements Doable<Integer,Integer> {
//業務邏輯
@Override
public Integer run(Integer integer) {
return integer*integer*integer;
}
public static void main(String[] args) {
MasterWorker<Integer,Integer> mw=MasterWorkerFactory.newStatic(new Pow3());
List<MasterWorker.ResultKey> keyList=new Vector<>();
for (int i = 0; i <100 ; i++) {
keyList.add(mw.submit(i));
}
mw.execute();
int re=0;
while (keyList.size()>0){
MasterWorker.ResultKey k=keyList.get(0);
Integer i=mw.result(k);
if(i!=null){
re+=i;
keyList.remove(0);
}
}
System.out.println(re);
}
}
為了增加系統的並行性,人們提出了執行緒,隨著應用程式日趨複雜,並行量要求越來越高,執行緒也變得沉重了起來。為了使系統有更高的並行度,便有了協程的概念。如果說執行緒是輕量級程序,那麼協程就是輕量級的執行緒。
相關框架:kilim。
介紹:略。
1、程式計數器
每一個執行緒都有一個獨立的程式計數器,用於記錄下一條要執行的指令,各個執行緒互不影響。
2、Java 虛擬機器器棧
它和 Java 執行緒在同一時間建立,儲存方法的區域性變數,部分結果,並參與方法的呼叫和返回。
public void test28(){ //gc無法回收,因為b還在區域性變數表中
{
byte[] b=new byte[6*1024*1024];
}
System.gc();
System.out.println("first explict gc over");
}
public void test29(){ //gc可以回收,因為變數a複用了b的字
{
byte[] b=new byte[6*1024*1024];
}
int a=0;
System.gc();
System.out.println("first explict gc over");
}
3、本地方法棧
本地方法棧和 java 虛擬機器器棧的功能很像,本地方法棧用於管理本地方法的呼叫。
4、Java 堆
幾乎所有的物件和陣列都是在堆中分配空間的。
5、方法區
主要儲存類的後設資料,所有執行緒共用的。其中最為重要的是類的型別資訊,常數池,靜態變數,域資訊,方法資訊。在 Hot Spot 虛擬機器器中,方法區也被稱為永久區。
1、參照計數法
如果有參照,計數器就加 1,當參照失效時,計數器就減 1.這種方法無法處理迴圈參照,不適用與 JVM 的回收。
2、標記-清除演演算法
第一階段,標記所有從根節點開始可達的物件。第二階段,清理所有未標記的物件。缺點是回收後的空間是不連續的。
3、複製演演算法
將原有的空間分為兩塊,將正在使用的那個空間的活物件複製到未使用的那個空間,在垃圾回收時,只需要清理正在使用的記憶體空間的所有物件,然後交換兩個記憶體空間的角色。缺點是要將系統記憶體折半。
4、標記壓縮演演算法
標記完成之後,將所有的存活物件壓縮到記憶體的一端,然後清除邊界外所有的空間。避免了碎片的產生。
5、增量演演算法
一次性的垃圾清理會造成系統長時間停頓,那麼就可以讓垃圾收集執行緒和應用程式執行緒交替執行。在垃圾回收的過程中間斷性的執行應用程式程式碼。
6、分代
對新生代使用複製演演算法,老年代使用標記-壓縮演演算法。
7、序列收集器
8、CMS 收集器
使用的是標記清除演演算法,並行的垃圾收集器
9、G1 收集器
基於標記-壓縮演演算法,目標是一款伺服器的收集器,在吞吐量和停頓控制上,要優於 CMS 收集器。
觀察 GC 情況:
package com.mmc.concurrentcystudy.test;
import java.util.HashMap;
public class StopWorldTest {
public static class MyThread extends Thread{
HashMap map=new HashMap();
@Override
public void run() {
try{
while (true){
if(map.size()*512/1024/1024>=400){ //防止記憶體溢位
map.clear();
System.out.println("clean map");
}
byte[] b1;
for (int i = 0; i <100 ; i++) {
b1=new byte[512]; //模擬記憶體佔用
map.put(System.nanoTime(),b1);
}
Thread.sleep(1);
}
}catch (Exception e){}
}
}
public static class PrintThread extends Thread{ //每毫秒列印時間資訊
public static final long starttime=System.currentTimeMillis();
@Override
public void run() {
try{
while (true){
long t=System.currentTimeMillis()-starttime;
System.out.println(t/1000+"."+t%1000);
Thread.sleep(100);
}
}catch (Exception e){}
}
}
public static void main(String[] args) {
MyThread t=new MyThread();
PrintThread p=new PrintThread();
t.start();
p.start();
}
}
1、將新物件預留在新生代
避免新物件因空間不夠進入了年老代,可以適當增加新生代的 from 大小。
2、大物件直接進入老年代
在大部分情況下,新物件分配在新生代是合理的,但是,對於大物件,很可能擾亂新生代,使得大量小的新生代物件因空間不足移到老年代。
3、設定物件進入老年代的年齡
4、穩定與震盪的堆大小
1、一般來說穩定的堆大小是對回收有利的,獲得一個穩定的堆大小的方法就是設定-Xms 和-Xmx 的大小一致。穩定的堆空間可以減少 GC 的次數,但是會增加每次 GC 的時間。
基於這樣的考慮,JVM 提供了壓縮和擴充套件堆空間的引數。
5、吞吐量優先案例
在擁有 4G 記憶體和 32 核 CPU 的計算機上,進行吞吐量的優化
6、使用大頁案例
在 Solaris 系統中,可以支援大頁的使用,大的記憶體分頁可以增強 CPU 的記憶體定址能力。
7、降低停頓案例
為了降低停頓,應儘可能將物件預留在新生代,因為新生代的 GC 成本遠小於老年代。
1、JIT 編譯引數
JIT 編譯器可以將位元組程式碼編譯成原生程式碼,從而提高執行效率。
2、堆快照
在效能優化中,分析堆快照是必不可少的環節。
-Xmx20M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d://log
)
3、錯誤處理
可以在系統發生 OOM 時,執行第三方指令碼。如重置系統
4、獲取 GC 資訊
5、類和物件跟蹤
6、控制 GC
7、使用大頁
8、壓縮指標
在 64 位虛擬機器器上,應用程式佔的記憶體要遠大於 32 位的,因為 64 位系統擁有更寬的定址空間,指標物件進行了翻倍。
1、tomcat 優化
2.當發現產生了 GC 時,看下是否需要增加新生代大小
set CATALINA_OPTS=%CATALINA_OPTS% -Xmx32M -Xms32M
3.如果發現有人使用了顯示的 GC 呼叫,可以禁止掉 set CATALINA_OPTS=%CATALINA_OPTS% -XX:+DesableExplicitGC
4.擴大新生代比例 set CATALINA_OPTS=%CATALINA_OPTS% -XX:NewRatio=2
5.給新生代使用並行回收 set CATALINA_OPTS=%CATALINA_OPTS% -XX:UseParallelGC
6.當確保 class 安全的時候,可以關閉 class 校驗 set CATALINA_OPTS=%CATALINA_OPTS% -Xverify:none
2、JMeter 介紹和使用
JMeter 是一款效能測試和壓力測試工具。
下載路徑:https://jmeter.apache.org/download_jmeter.cgi
下載解壓之後,進入 bin 目錄,點選 jmeter.bat 啟動。
右鍵新增執行緒組
右鍵新增請求
新增結果檢視
這裡有很多結果檢視,都可以選來試試。
3、案例
確認堆大小(-Xmx,-Xms),合理分配新生代和老年代(-XX:NewRatio,-Xmn,-XX:SurvivorRatio),確定永久區大小(-XX:PermSize,-XX:MaxPermSize),選擇垃圾收集器,除此之外,禁用顯示的 GC,禁用類元素回收,禁用類驗證對效能也有提升。
實戰的調優:
1、top 命令
2、sar 命令
3、vmstat 命令
統計 CPU、記憶體情況
4、iostat 命令
統計 IO 資訊
5、pidstat
可以監控執行緒的
grant codebase "file:D:/Java/jdk1.8.0_112/lib/tools.jar" {
permission java.security.AllPermission;
};
2、執行開啟
jstatd -J-Djava.security.policy=D:/jstatd.all.policy
3、新開一個 cmd 視窗,執行 jps localhost:1099 即可遠端監聽
8.hprof 工具,程式執行時加上-agentlib:hprof=cpu=times,interval=10 可以匯出函數執行時間。還可以使用-agentlib:hprof=heap=dump,format=b,file=d:/core.hprof 匯出檔案
1、BTrace 外掛,可在不修改程式碼情況下給程式碼加紀錄檔
/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class TracingScript {
/* put your code here */
private static long startTime=0;
//方法開始時呼叫
@OnMethod(clazz="com.mmc.concurrentcystudy.test.BTraceTest",method="writeFile") //監控任意類的writeFile方法
public static void startMethod(){
startTime=timeMillis();
}
@OnMethod(clazz="com.mmc.concurrentcystudy.test.BTraceTest",method="writeFile",location=@Location(Kind.RETURN)) //方法返回時觸發
public static void endM(){
print(strcat(strcat(name(probeClass()),"."),probeMethod()));
print("[");
print(strcat("Time taken:",str(timeMillis()-startTime)));
println("]");
}
}
範例 2:
/* BTrace Script Template */
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class TracingScript {
/* put your code here */
@OnMethod(clazz="/.*Test/",location=@Location(value=Kind.LINE,line=27)) //,監控Test結尾的類,指定程式執行到第27行觸發
public static void onLine(@ProbeClassName String pcn,@ProbeMethodName String pmn,int line){
print(Strings.strcat(pcn,"."));
print(Strings.strcat(pmn,":"));
println(line);
}
}
範例 3:每秒執行
@BTrace
public class TracingScript {
/* put your code here */
@OnTimer(1000) //每秒鐘執行
public static void getUpTime(){
println(Strings.strcat("l000 msec:",Strings.str(Sys.VM.vmUptime()))); //虛擬機器器啟動時間
}
@OnTimer(3000)
public static void getStack(){
jstackAll(); //匯出執行緒堆疊
}
}
範例 4:獲取引數
@BTrace
public class TracingScript {
/* put your code here */
@OnMethod(clazz="com.mmc.concurrentcystudy.test.BTraceTest",method="writeFile")
public static void any(String filename){
print(filename);
}
}
1、下載 mat
https://www.eclipse.org/mat/
2、深堆和淺堆
淺堆:一個物件結構所佔用的記憶體大小。
深堆:一個物件被 GC 後,可以真實釋放的記憶體大小
略