Java執行緒總結---第一天
執行緒和程序各自有什麼區別和優劣:
-
程序是資源分配的最小單位,執行緒是程式執行的最小單位
-
程序有自己的獨立地址空間,每啟動一個程序,系統就會為它分配地址空間,建立資料表來維護程式碼段、堆疊段和資料段,這種操作非常昂貴。而執行緒是共享程序中的資料的,使用相同的地址空間,因此CPU切換一個執行緒的花費遠比程序要小很多,同時建立一個執行緒的開銷也比程序要小很多,執行緒的上下文切換的效能消耗要小於程序。
-
執行緒之間的通訊更方便,同一程序下的執行緒共享全域性變數、靜態變數等資料。
-
多程序程式更健壯,多執行緒程式只要有一個執行緒死掉,整個程序也死掉了,而一個程序死掉並不會對另外一個程序造成影響,因為程序有自己獨立的地址空間。
並行與併發
併發是沒有時間上的重疊的,兩個任務是交替執行的,由於切換的非常快,對於外界呼叫者來說相當於同一時刻多個任務一起執行了;而並行可以看到時間上是由重疊的,也就是說並行才是真正意義上的同一時刻可以有多個任務同時執行。
小程式demo
public class Demo extends Thread { @Override public void run() { while (true){ System.out.println(this.currentThread().getName()); } } public static void main(String[] args) { Demo demo= new Demo(); demo.start(); } } public class Demo implements Runnable { @Override public void run() { while (true){ System.out.println("hello thread"); } } public static void main(String[] args) { Demo demo= new Demo(); Thread thread=new Thread(demo,"t1"); thread.start(); } } public class Demo{ public int count = 0; public void print() { while (true){ System.out.println(count++); } } public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { new Demo().print(); } }).start(); new Thread(new Runnable() { @Override public void run() { new Demo().print(); } }).start(); } }
synchronized鎖範圍
普通同步方法,鎖是當前例項物件
靜態同步方法,鎖是當前類的class物件
同步方法塊,鎖是括號裡面的物件
public class Demo{ public synchronized void synsMethod1(){ System.out.println("method1---"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } synsMethod3(); } public synchronized void synsMethod2(){ System.out.println("method2---"); } public synchronized void synsMethod3(){ System.out.println("method3---"); } public static void main(String[] args) { Demo demo=new Demo(); new Thread(new Runnable() { @Override public void run() { demo.synsMethod1(); } }).start(); new Thread(new Runnable() { @Override public void run() { demo.synsMethod2(); } }).start(); } }
輸出結果
method1---
method3---
method2---
再如下
public class Demo{
public synchronized void synsMethod1(){
System.out.println("method1---");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synsMethod3();
}
public synchronized static void synsMethod2(){
System.out.println("method2---");
}
public synchronized void synsMethod3(){
System.out.println("method3---");
}
public static void main(String[] args) {
Demo demo=new Demo();
new Thread(new Runnable() {
@Override
public void run() {
demo.synsMethod1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
demo.synsMethod2();
}
}).start();
}
}
輸出結果(其中一種可能)
method1---
method2---
method3---
所以說靜態方法的鎖和非靜態方法的鎖是不一樣的
Synchronized鎖重入
關鍵字Synchronized擁有鎖重入的功能,也就是在使用Synchronized的時候, 當一個執行緒得到一個物件的鎖後,在該鎖裡執行程式碼的時候可以再次請求該物件的鎖 時可以再次得到該物件的鎖。
當執行緒請求一個由其它執行緒持有的物件鎖時,該執行緒會阻塞,而當執行緒請求由自己持有的物件鎖時,如果該鎖是重入鎖,請求就會成功,否則阻塞。
一個簡單的例子就是:在一個Synchronized修飾的方法或程式碼塊的內部呼叫本 類的其他Synchronized修飾的方法或程式碼塊時,是永遠可以得到鎖的。
/**
* @program: demo
* @description:
* @author: lee
* @create: 2019-02-25
**/
public class Demo{
public synchronized void synsMethod1(){
System.out.println("method1---");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synsMethod2();
}
public synchronized void synsMethod2(){
System.out.println("method2---");
}
public static void main(String[] args) {
Demo demo=new Demo();
new Thread(new Runnable() {
@Override
public void run() {
demo.synsMethod1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
demo.synsMethod2();
}
}).start();
}
}
為什麼要引入可重入鎖這種機制哪?
假如有一個執行緒T獲得了物件A的鎖,那麼該執行緒T如果在未釋放前再次請求該物件的鎖時,如果沒有可重入鎖的機制,是不會獲取到鎖的,這樣的話就會出現死鎖的情況,所以最大的作用是避免死鎖。
volatile與synchronized的
volatile關鍵字的作用就是強制從公共堆疊中取得變數的值,而不是執行緒私有的資料棧中取得變數的值。
關鍵字volatile是執行緒同步的輕量級實現,效能比synchronized要好,並且volatile只能修飾變數,而synchronized可以修飾方法,程式碼塊等。
多執行緒訪問volatile不會發生阻塞,而synchronized會發生阻塞。
volatile可以保證資料的可見性,但不可以保證原子性(不是執行緒安全的),而synchronized可以保證原子性,也可以間接保證可見性,因為他會將私有記憶體和公共記憶體中的資料做同步。
volatile解決的是變數在多個執行緒之間的可見性,而synchronized解決的是多個執行緒之間訪問資源的同步性。
ThreadLocal
ThreadLocal提供了執行緒的區域性變數,每個執行緒都可以通過set()和get()來對這個區域性變數進行操作,但不會和其他執行緒的區域性變數進行衝突,實現了執行緒的資料隔離。
簡要言之:往ThreadLocal中填充的變數屬於當前執行緒,該變數對其他執行緒而言是隔離的。
最典型的應用
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 採用ThreadLocal封裝Connection
*
* @author Administrator
*
*/
public class ConnectionManager {
//定義ThreadLocal靜態變數,確定存取型別為Connection
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();
/**
* 得到Connection
* @return
*/
public static Connection getConnection() {
Connection conn = connectionHolder.get();
//如果在當前執行緒中沒有繫結相應的Connection
if (conn == null) {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
String url = "jdbc:oracle:thin:@localhost:1521:bjpowern";
String username = "drp1";
String password = "drp1";
conn = DriverManager.getConnection(url, username, password);
//將Connection設定到ThreadLocal
connectionHolder.set(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
return conn;
}
/**
* 關閉資料庫連線方法
* @return
*/
public static void closeConnection() {
Connection conn = connectionHolder.get();
if (conn != null) {
try {
conn.close();
//從ThreadLocal中清除Connection
connectionHolder.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 關閉資料庫連線方法
* @return
*/
public static void close(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Statement pstmt) {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(ResultSet rs ) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 事務開啟
* @return
*/
public static void beginTransaction(Connection conn) {
try {
if (conn != null) {
if (conn.getAutoCommit()) {
conn.setAutoCommit(false); //手動提交
}
}
}catch(SQLException e) {}
}
/**
* 事務提交
* @return
*/
public static void commitTransaction(Connection conn) {
try {
if (conn != null) {
if (!conn.getAutoCommit()) {
conn.commit();
}
}
}catch(SQLException e) {}
}
/**
* 事務回滾
* @return
*/
public static void rollbackTransaction(Connection conn) {
try {
if (conn != null) {
if (!conn.getAutoCommit()) {
conn.rollback();
}
}
}catch(SQLException e) {}
}
}
初始化預設值
public class ThreadlLocalDemo {
/**
* 初始化一個ThreadLocal物件,並且初始值設定為0
*/
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
public int getNextNumber() {
threadLocal.set(threadLocal.get() + 1);
return threadLocal.get();
}
static class DemoThread extends Thread {
ThreadlLocalDemo threadlLocalDemo = null;
public DemoThread(ThreadlLocalDemo localDemo) {
this.threadlLocalDemo = localDemo;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) { // 每個執行緒列印 5個值
System.out.println("Thread:" + Thread.currentThread().getName()
+ ",threadlLocalDemo:" + threadlLocalDemo.getNextNumber());
}
}
}
public static void main(String[] args) {
ThreadlLocalDemo threadlLocalDemo = new ThreadlLocalDemo();
DemoThread demoThread = new DemoThread(threadlLocalDemo);
DemoThread demoThread2 = new DemoThread(threadlLocalDemo);
DemoThread demoThread3 = new DemoThread(threadlLocalDemo);
DemoThread demoThread4 = new DemoThread(threadlLocalDemo);
demoThread.start();
demoThread2.start();
demoThread3.start();
demoThread4.start();
}
}
ThreadLocal實現的原理
首先,我們來看一下ThreadLocal的set()方法,因為我們一般使用都是new完物件,就往裡邊set物件了
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
上面有個ThreadLocalMap,我們去看看這是什麼?
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
//....很長
}
通過上面我們可以發現的是ThreadLocalMap是ThreadLocal的一個內部類。用Entry類來進行儲存
我們的值都是儲存到這個Map上的,key是當前ThreadLocal物件!
如果該Map不存在,則初始化一個:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
如果map存在
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
Thread維護了ThreadLocalMap變數
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null
從上面又可以看出,ThreadLocalMap是在ThreadLocal中使用內部類來編寫的,但物件的引用是在Thread中! 於是我們可以總結出:Thread為每個執行緒維護了ThreadLocalMap這麼一個Map,而ThreadLocalMap的key是LocalThread物件本身,value則是要儲存的物件。
有了上面的基礎,我們看get()方法就一點都不難理解了:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocal原理總結
每個Thread維護著一個ThreadLocalMap的引用
ThreadLocalMap是ThreadLocal的內部類,用Entry來進行儲存
呼叫ThreadLocal的set()方法時,實際上就是往ThreadLocalMap設定值,key是ThreadLocal物件,值是傳遞進來的物件
呼叫ThreadLocal的get()方法時,實際上就是往ThreadLocalMap獲取值,key是ThreadLocal物件
ThreadLocal本身並不儲存值,它只是作為一個key來讓執行緒從ThreadL