Java併發編程藝術 4 Java併發編程基礎
第四章 Java併發編程基礎
優先級
操作系統使用時分的形式調度運行的線程。每個線程會被分配到若干時間片,當線程時間片用戶線程發生調度,執行下一個線程。當前線程等待下次分配。
通過成員變量priority設置優先級。範圍1-10。默認5。數值越大優先級越高,10最高。
線程的狀態
Java線程可能處於一下6中狀態,在給定的某一時刻只能處於一種狀態。
1.實例化後,調用start()之前都處於初始狀態。
2.當線程調用start()後,線程進入運行狀態
3.如果線程中有對象執行了wait()方法,當前線程進入等待狀態。需要notify()方法喚醒
4.如果線程中有對象執行了wait(long)、sleep(long)方法等,進入超時等待狀態。需要notify()方法喚醒或者超時時間到。對等待狀態設置了超時時間
5.如果調用同步方法,在沒有獲得鎖的情況下會進入阻塞狀態。
6.線程執行完進入終止狀態。
Java將運行和就緒兩個狀態合併爲運行狀態。
阻塞狀態是進入synchronized方法/代碼塊(獲取鎖)時的狀態
Daemon線程(守護線程)
Java可以創建兩種線程:用戶線程和守護線程。用戶線程就是一般創建的線程。守護線程是後臺線程,可以使JVM的線程也可以自己創建守護線程。
Daemon線程是一種支持型線程,當線程只剩下守護線程時,JVM就會退出。如果還有其他的用戶線程,JVM就不會退出。
Thread.setDaemon(true)來設置爲Daemon線程。
注意:要在線程運行之前設置爲守護線程,啓動之後設置就沒用了
注意:守護線程會因爲JVM關閉,立即終止。所以守護線程中的finally塊不一定會執行。不能用來確保執行close操作和清理資源的邏輯。
Thread初始化
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
。。。。。
Thread parent = currentThread(); //當前線程就是該線程的父線程,有當前線程創建子線程
。。。。
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority(); //使用父線程的daemon和priority
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
//使用靜態變量,使用++來獲取下一個線程id
tid = nextThreadID();
}
啓動線程
Thread通過start()方法啓動線程,方法含義:當前線程(pattern線程)同步告知JVM,只要線程規劃器空閒,應立即調用start()方法。
調用了 start0()。 private native void start0();
注意:對於自定義的線程,最好可以添加名稱,方便排查錯誤或者分析程序。
volatile和synchronized關鍵字
支持多個線程同時訪問同一個對象或者成員變量。每個線程擁有對象的拷貝,保存在線程內存中。每個線程內存對其他線程不可見。
使用volatile可以用來修飾字段,告知程序線程中對volatile變量的修改,立即更新到主內存中。可以線程中使用的對象是主存中最新的狀態。但是並不能解決同步問題(同時獲取到同一個值)。
synchronized可以修飾方法或者同步塊的形式來使用,確保多個線程在同一時刻,只有一個線程處於同步塊或者同步方法中。(對象鎖)。確保線程對變量訪問的可見性和排他性。
等待和通知(wait/notify)
public class WaitNotify {
public static Object lock = new Object();
public static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(new Wait()).start();
Thread.sleep(2000);
new Thread(new Notify()).start();
}
static class Wait implements Runnable {
@Override
public void run() {
//加鎖,擁有lock的Monitor
synchronized (lock) {
while (flag) {
try {
System.out.println(Thread.currentThread() + "flag is true" );
lock.wait(); //釋放synchronized (lock) 擁有的鎖,釋放同步鎖
} catch (Exception e) {e.printStackTrace();}
}
}
//Notify線程設置flag爲false,結束工作
System.out.println(Thread.currentThread() + "flag is false");
}
}
static class Notify implements Runnable {
@Override
public void run() {
//因爲lock.wait()放棄了同步鎖,所以Notify線程能獲取lock的鎖,擁有lock的Monitor
synchronized (lock) {
System.out.println(Thread.currentThread() + " hold lock , notify" );
lock.notify(); //喚醒等待的線程(Wait)
flag = false;
try {Thread.sleep(5000);} catch (InterruptedException e) {}
}
System.out.println(Thread.currentThread() + "flag is false" );
//當Wait線程執行完Synchronized同步塊後,進入同步代碼塊
synchronized (lock) {
System.out.println(Thread.currentThread() + " hold lock again, notify" + System.currentTimeMillis());
}
}
}
}
1.使用wait(),notify(),notifyAll(),需要先對調用對象加鎖(可以就是說要放在synchronized代碼塊中)
2.從wait()方法返回得前提是獲得了調用對象的鎖(也就是所其他線程調用了notify(),並且其他線程釋放鎖)
3.調用wait()方法後,線程從RUNNING變爲WAITING,放到對象的線程等待隊列。
4.其他線程調用notify() 方法後,線程被喚醒,從線程等待池中進入等鎖池,從WAITING變爲BLOCKED。等待其他線程執行完畢,釋放同步鎖。
等待/通知的範式
等待方
synchronized (對象) { //1.獲取對象鎖·
while (條件不滿足) { //2.如果條件不滿足,那麼調用對象的wait()方法,被通知後仍要檢查條件(自旋鎖)
對象.wait()
}
對應的處理邏輯 //3.條件滿足則執行對應的邏輯
}
通知方
synchronized (對象) { //1.獲取對象鎖·
2.改變條件
對象.notifyAll() //3.通知所有等待池中的線程
}
管道輸入/輸出
主要用於線程之間的數據傳輸。主要有一下4個實現 PipeOutputStream,PipeInputStream和PipeReader,PipeWriter前面兩者面向字節流,後面兩者面向字符
public class PipeInOut {
public static void main(String[] args) throws IOException {
PipedReader in = new PipedReader();
PipedWriter out = new PipedWriter();
out.connect(in); //對應Pipe類型的流需要 使用connect進行綁定。沒有將輸入輸出進行綁定,對流的的訪問會有問題。
new Thread(new Send(out)).start();
new Thread(new Print(in)).start();
}
static class Send implements Runnable{
private PipedWriter out ;
public Send(PipedWriter out) {
super();
this.out = out;
}
@Override
public void run() {
int receive = 0;
try {
while((receive = System.in.read() ) != -1){
out.write(receive);
}
} catch (IOException e) {e.printStackTrace();
} finally{
out.close();
}
}
}
static class Print implements Runnable{
private PipedReader in ;
public Print(PipedReader in) {
super();
this.in = in;
}
@Override
public void run() {
int receive = 0;
try {
while((receive = in.read() ) != -1){
System.out.println((char)receive); //接受到的爲int型,需要轉化爲Char
}
} catch (IOException e) {e.printStackTrace();}
}
}
}
Thread.join()方法
如果當前線程A,執行了thread.join()。當前線程A會等待thread執行完以後再從join()返回。還提供了join( long )的具有超時特性的方法。
join()會使主線程等待,但是本身的線程沒有影響。
public class JoinTest {
public static void main(String[] args) {
Thread pervious = Thread.currentThread();
for(int i=0; i<10 ;i++){
Thread thread = new Thread(new Domino(pervious));
thread.start();
pervious = thread;
}
System.out.println(Thread.currentThread().getName() + " terminate");
}
static class Domino implements Runnable{
Thread thread;
public Domino(Thread thread) {
this.thread = thread;
}
public void run() {
try {
thread.join(); //當前線程會被阻塞,thread對象線程 不會被阻塞
} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName() + " terminate");
}
}
}
join主線程等待子線程執行完畢
public static void main(String[] args){
System.out.println("main thread starting...");
List<MyThread> list = new ArrayList<MyThread>();
for (int i = 1; i <= 5; i++) {
MyThread my = new MyThread("Thrad " + i);
my.start();
list.add(my);
}
try{
for (MyThread my : list){
my.join(); //main線程會等待,但是my線程並沒有影響
}
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("main thread end...");
}
}
ThreadLocal
public class ThreadLocalTest {
String name;
static ThreadLocal<Integer> sint = new ThreadLocal<Integer>(){
protected Integer initialValue(){
return (int)(Math.random()*10);
}
};
ThreadLocal<Integer> oint = new ThreadLocal<Integer>(){
protected Integer initialValue(){
return (int)(Math.random()*10);
}
};
public static void main(String[] args) throws InterruptedException {
ThreadLocalTest e1 = new ThreadLocalTest("e1");
ThreadLocalTest e2 = new ThreadLocalTest("e2");
new Thread(new MyRunnable(e1,e2)).start();
Thread.sleep(100);
new Thread(new MyRunnable(e1,e2)).start();
}
static class MyRunnable implements Runnable{
打印e1,e2的靜態變量和成員變量
}
可以看出來:
在同一線程中,static變量的值是一樣的。但在不同線程中,static的值不同。
在同一線程中,成員變量的值是不一樣的。在不同線程中就更不一樣了。
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.