程式:完成特定功能的一系列有序指令的集合
一個可執行檔案
程式碼段+數據段
進程:程式的一次動態執行過程
程式碼段+數據段+堆疊段+PCB
一個進程只對應一個程式
一個程式可以有很多進程
執行緒:程式的一個可執行路線
進程是資源競爭的基本單位
執行緒是程式執行的最小單位
執行緒共用進程數據,例如進程各種ID,地址空間,信號處理器表格,檔案描述符表
但也擁有自己的一部分數據(CPU狀態),執行緒ID,一組暫存器(堆疊地址指針),棧,errno,信號狀態,優先順序
1)繼承THread
2)實現Runnable介面
1)同步方法 同步監視器爲當前物件this
2)同步程式碼快(同步監視器/共用資源的物件){
}
3)過多的同步就會導致死鎖
編寫兩個執行緒,一個執行緒列印1-52的整數,另一個執行緒列印字母A-Z。列印順序爲12A34B56C…5152Z。即按照整數和字母的順序從小到大列印,並且每列印兩個整數後,列印一個字母,交替回圈列印,直到列印到整數52和字母Z結束。
要求:
1)編寫列印類Printer,宣告私有屬性index,初始值爲1,用來表示是第幾次列印。
2)在列印類Printer中編寫列印數位的方法print(int i),3的倍數就使用wait()方法等待,否則就輸出 i ,使用notifyAll()進行喚醒其他執行緒。
3)在列印類Printer中編寫列印字母的方法print(char c),不是3的倍數就等待,否則就列印輸出字母c,使用notifyAll()進行喚醒其他執行緒。
4)編寫列印數位的執行緒 NumberPrinter 繼承 Thread 類,宣告
私有屬性 private Printer p;在構造方法中進行賦值,實現父類別
的 run 方法,呼叫 Printer 類中的輸出數位的方法。
5) 編寫列印字母的執行緒 LetterPrinter 繼承 Thread 類,宣告私
有屬性 private Printer p;在構造方法中進行賦值,實現父類別的
run 方法,呼叫 Printer 類中的輸出字母的方法。
6) 編寫測試類 Test,建立列印類物件,建立兩個執行緒類物件,
啓動執行緒。
checkPointThread
LetterPrinter.java
package com.bjsxt.thread;
public class LetterPrinter implements Runnable{
private Printer printer;
public LetterPrinter(Printer printer) {
this.printer=printer;
}
@Override
public void run() {
for(char c='A';c<='Z';c++){
printer.print(c);
}
}
}
NumberPrinter.java
package com.bjsxt.thread;
public class NumberPrinter implements Runnable {
private Printer printer;
public NumberPrinter(Printer printer) {
this.printer=printer;
}
@Override
public void run() {
for(int i=1;i<=52;i++){
printer.print(i);
}
}
}
Printer.java
package com.bjsxt.thread;
public class Printer {
private int index=1;//用於統計第幾次列印
public synchronized void print(int number){
while(index%3==0){
try {
super.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.print(number);
index++;
super.notifyAll();//喚醒在Printer這個物件上的所有的等待的執行緒
}
public synchronized void print(char letter){
while(index%3!=0){
try {
super.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.print(""+letter);
index++;
super.notifyAll();
}
}
Test.java
package com.bjsxt.thread;
public class Test {
public static void main(String[] args) {
//(1)建立共用資源的物件
Printer p=new Printer();
NumberPrinter np=new NumberPrinter(p);
LetterPrinter lp=new LetterPrinter(p);
//建立代理類,並啓執行緒
new Thread(np).start();
new Thread(lp).start();
}
}
1)沒有返回值
2)不支援泛型
3)異常必須處理
實現Callable介面,重寫call方法
Callable功能更加強大
1)Future介面位於 java.util.concurrent 包中,可以對具體
Runnable、Callable任務的執行結果進行取消(cancel方法,嘗試取消執行任務)、查詢是否完成(isDone 方法)、獲取結果(get方法,等待完成,然後檢索其結果)等。
2) FutrueTask 是 Futrue 介面的唯一的實現類
3) FutureTask 同時實現了 Runnable, Future 介面。它既可以
作爲 Runnable 被執行緒執行,又可以作爲 Future 得到
Callable 的返回值
callableProject2
MyCallable.java
package com.bjsxt.callable;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
String [] str={"apple","banana","orange","pear","grape"};
int index=(int)(Math.random()*5);
return str[index];
}
}
Test.java
package com.bjsxt.callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//(1)建立任務
MyCallable call=new MyCallable();
//(2)交能任務管理
/**工作管理員是一個實現類,實現了RunnableFutrue介面,
* RunnableFutrue是Futrue與Runnable介面的子介面*/
FutureTask<String> task=new FutureTask<>(call); //可以看成是Runnable介面的實現類
//建立代理類並啓動執行緒
Thread t=new Thread(task);
t.start();
System.out.println("獲取結果:"+task.get());
//判斷任務是否執行完成
System.out.println("任務是否執行完成:"+task.isDone());
}
}
Lock鎖:對需要上鎖的地方上鎖
1)JDK1.5後新增的功能
2)與Synchronized相比,Lock可提供多種鎖方案,更靈活
3) Java.util.concurrent.locks 中的 Lock 是一個介面,它的實現類是一個Java類,而不是作爲語言的特性(關鍵字)來實現
注意:如果同步程式碼有異常,要將unLock()放到finally中
1)建立 Lock物件
2)呼叫 lock()方法上鎖
3)呼叫 unlock()方法解鎖
lockProject
CountRunnable.java
package com.bjsxt.account;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class CountRunnable implements Runnable {
private int count=0;//預設值
//建立一個Lock物件
Lock lock=new ReentrantLock();
@Override
public void run() {
for(int i=0;i<10;i++){
//synchronized (this) {
try{
lock.lock();//加鎖
count++;
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"執行操作:count="+count);
}finally{
//解鎖
lock.unlock();
}
}
//}
}
}
Test.java
package com.bjsxt.account;
public class Test {
public static void main(String[] args) {
CountRunnable cr=new CountRunnable();
//代理類的物件
Thread t1=new Thread(cr,"A");
Thread t2=new Thread(cr,"B");
Thread t3=new Thread(cr,"C");
t1.start();
t2.start();
t3.start();
}
}
建立和銷燬物件是非常耗費時間的
建立物件:需要分配記憶體等資源
銷燬物件:雖然並不需要程式設計師操心,但是垃圾回收器會在後台一直跟蹤並銷燬
對於經常建立和銷燬、使用量特別大的資源,比如併發情況下的執行緒,對效能影響很大。
思路:建立好多個執行緒,放入執行緒池中,使用時直接獲取參照,不使用時放回池中。可以避免頻繁建立銷燬、實現重複利用。
生活案例:共用單車
技術案例:執行緒池、數據庫連線池
JDK1.5 起,提供了內建執行緒池
1)提高響應速度(減少了建立新執行緒的時間)
2)降低資源消耗(重複利用執行緒池中執行緒,不需要每次都建立)
3)提高執行緒的可管理性:避免執行緒無限制建立、從而銷燬系統資源,降低系統穩定性,甚至記憶體溢位或者CPU耗盡。
1)需要大量執行緒,並且完成任務的時間短
2)對效能要苛刻
3)接受突發性的大量請求
threadPoolProject
Test1.java
package com.bjsxt.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test1 {
public static void main(String[] args) {
//如何建立一個執行緒池
//(1)建立一個執行緒池,執行緒池中只有一個執行緒物件
//ExecutorService pool1=Executors.newSingleThreadExecutor();
//(2)建立一個執行緒池,執行緒池中有執行緒的數量固定
//ExecutorService pool1=Executors.newFixedThreadPool(10);
//(3)建立一個執行緒池,執行緒池中的執行緒的數量可以動態的改變
ExecutorService pool1=Executors.newCachedThreadPool();
/**使用執行緒池執行大量的Runnable命令*/
for(int i=0;i<20;i++){
final int n=i;
//使用匿名內部類//任務
Runnable command=new Runnable() {
@Override
public void run() {
System.out.println("開始執行:"+n);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("執行結束:"+n);
}
};//任務結束
//將任務交給執行緒池中的執行緒去執行
pool1.execute(command);
}
//關閉執行緒池
pool1.shutdown();
}
}
Test2.java
package com.bjsxt.pool;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 如何建立一個執行緒池
// (1)建立一個執行緒池,執行緒池中只有一個執行緒物件
//ExecutorService pool1=Executors.newSingleThreadExecutor();
// (2)建立一個執行緒池,執行緒池中有執行緒的數量固定
ExecutorService pool1=Executors.newFixedThreadPool(10);
// (3)建立一個執行緒池,執行緒池中的執行緒的數量可以動態的改變
//ExecutorService pool1 = Executors.newCachedThreadPool();
//建立一個集合
List<Future> list=new ArrayList<Future>();
/**使用執行緒池執行大量的Callable任務*/
for(int i=0;i<20;i++){
//使用匿名內部類
//建立任務
Callable<Integer> task=new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(2000);
return (int)(Math.random()*10)+1;
}
}; //任務結束
//將任務交能執行緒池
Future f=pool1.submit(task);
list.add(f);
//System.out.println(f.get());
}
System.out.println("ok?");
//遍歷集合
for(Future ff:list){
System.out.println(ff.get());
}
System.out.println("OK!");
//關閉執行緒池
pool1.shutdown();
}
}
任務:就是事情
排程:在不同的時間點或者在指定的時間點或者間隔多長時
間我去執行這個任務。
就是生活中的鬧鐘
Timer 類:位於 java.util 包中
實現時間的動態重新整理
TimerProject
Clock.java
package com.bjsxt.timer;
import java.util.Date;
import java.util.TimerTask;
/**
* 任務
* @author Administrator
*
*/
public class Clock extends TimerTask{
long time=1000;//1秒
@Override
public void run() {
Date date=new Date(time);
System.out.println(date.toLocaleString());
time+=1000;
}
}
TestTimer.java
package com.bjsxt.timer;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TestTimer {
public static void main(String[] args) {
//(1)建立Timer的物件
Timer t=new Timer();
//(2)呼叫schedule()方法去執行任務
//建立任務類的物件
TimerTask task=new Clock();
//要執行的任務,第二個參數是任務的執行開始時間 ,第三個參數是每隔多長時間執行一次
t.schedule(task, new Date(System.currentTimeMillis()+1000), 1000);
}
}
ThreadLocal 直譯爲「本地執行緒」,其實它就是一容器,用於
存放執行緒的區域性變數
作用:爲解決多執行緒程式的併發問題
實現一個序列號的生成器程式
threadLocalProject
MyThread.java
package com.bjsxt.test;
public class MyThread implements Runnable {
private Sequence seq;
public MyThread(Sequence seq) {
this.seq=seq;
}
@Override
public void run() {
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+"-->number"+seq.getNumber());
}
}
}
Sequence.java
package com.bjsxt.test;
public interface Sequence {
public int getNumber();//每次呼叫時獲得一個數,下次呼叫時,這個數自增
}
SequenceImple.java
package com.bjsxt.test;
public class SequenceImple implements Sequence {
private static int number=0;
@Override
public int getNumber() {
number++;
return number;
}
}
SequenceImple2.java
package com.bjsxt.test;
public class SequenceImple2 implements Sequence {
private static ThreadLocal<Integer> numberContainer=new ThreadLocal<Integer>(){
protected Integer initialValue() {
return 0;
}
};
@Override
public int getNumber() {
numberContainer.set(numberContainer.get()+1);
return numberContainer.get();
}
}
Test.java
package com.bjsxt.test;
public class Test {
public static void main(String[] args) {
//(1)建立共用資源的物件
//Sequence seq=new SequenceImple();
Sequence seq=new SequenceImple2();
//(2)建立執行緒類的物件
MyThread m=new MyThread(seq);
//建立三個代理,並啓動執行緒
new Thread(m,"A").start();
new Thread(m,"B").start();
new Thread(m,"C").start();
}
}
ThreadLocal 的使用場景爲:用來解決數據庫連線、Session
管理等
當你在一個類中使用 static 成員變數時,一定要問自己這
個 static 成員變數需要考慮「執行緒安全嗎?」(也就是說多個
執行緒需要自己獨立的 static 成員變數嗎?)如果需要那就需
要使用 ThreadLocal。
threadLocalProject_2
Dao.java
package com.bjsxt.dbutil;
import java.sql.Connection;
public class Dao {
public void insert(){
//獲取連線
//System.out.println("Dao.insert()"+Thread.currentThread().getName()+DBUtil.getConnection());
//Connection conn=new DBUtil2().getConnection();
Connection conn=DBUtil3.getConnection();
System.out.println("Dao.insert()"+Thread.currentThread().getName()+conn);
}
public void delete(){
//獲取連線
//System.out.println("Dao.delete()"+Thread.currentThread().getName()+DBUtil.getConnection());
//Connection conn=new DBUtil2().getConnection();
Connection conn=DBUtil3.getConnection();
System.out.println("Dao.delete()"+Thread.currentThread().getName()+conn);
}
public void update(){
//獲取連線
//System.out.println("Dao.update()"+Thread.currentThread().getName()+DBUtil.getConnection());
//Connection conn=new DBUtil2().getConnection();
Connection conn=DBUtil3.getConnection();
System.out.println("Dao.update()"+Thread.currentThread().getName()+conn);
}
public void select(){
//獲取連線
//System.out.println("Dao.select()"+Thread.currentThread().getName()+DBUtil.getConnection());
//Connection conn=new DBUtil2().getConnection();
Connection conn=DBUtil3.getConnection();
System.out.println("Dao.select()"+Thread.currentThread().getName()+conn);
}
}
DBUtil.java
package com.bjsxt.dbutil;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtil {
private static final String DRIVER="com.mysql.jdbc.Driver";
private static final String USER="root";
private static final String PWD="root";
private static final String URL="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
//定義一個數據庫連線
private static Connection conn=null;
//獲取連線
public static Connection getConnection(){
try {
Class.forName(DRIVER);
if(conn==null){
conn=DriverManager.getConnection(URL, USER, PWD);
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
//關閉連線的方法
public static void colseConnection(){
if (conn!=null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
System.out.println(getConnection());
}
}
DBUtil2.java
package com.bjsxt.dbutil;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtil2 {
private static final String DRIVER="com.mysql.jdbc.Driver";
private static final String USER="root";
private static final String PWD="root";
private static final String URL="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
//定義一個數據庫連線
private Connection conn=null;
//獲取連線
public Connection getConnection(){
try {
Class.forName(DRIVER);
if(conn==null){
conn=DriverManager.getConnection(URL, USER, PWD);
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
//關閉連線的方法
public void colseConnection(){
if (conn!=null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
DBUtil3.java
package com.bjsxt.dbutil;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtil3 {
private static final String DRIVER="com.mysql.jdbc.Driver";
private static final String USER="root";
private static final String PWD="root";
private static final String URL="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
//定義一個數據庫連線
private static Connection conn=null;
private static ThreadLocal<Connection> connContainer=new ThreadLocal<Connection>(){
protected Connection initialValue() {
try {
Class.forName(DRIVER);
if(conn==null){
conn=DriverManager.getConnection(URL, USER, PWD);
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
};
};
//獲取連線
public static Connection getConnection(){
return connContainer.get(); //獲取連線
}
//關閉連線的方法
public static void colseConnection(){
if (conn!=null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
System.out.println(getConnection());
}
}
Test.java
package com.bjsxt.dbutil;
public class Test {
public static void main(String[] args) {
MyThread my=new MyThread();
for(int i=0;i<3;i++){
new Thread(my).start();
}
}
}
class MyThread implements Runnable{
Dao d=new Dao();
@Override
public void run() {
d.insert();
d.update();
d.delete();
d.select();
}
}