// 經過實現Runnable接口來建立線程類 public class SecondThread implements Runnable { private int i ; // run方法一樣是線程執行體 public void run() { for ( ; i < 100 ; i++ ) { // 當線程類實現Runnable接口時, // 若是想獲取當前線程,只能用Thread.currentThread()方法。 System.out.println(Thread.currentThread().getName() + " " + i); } } public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 20) { SecondThread st = new SecondThread(); // ① // 經過new Thread(target , name)方法建立新線程 Thread thread = new Thread(st , "新線程1"); thread.join(); } } } }
join()方法,當某個程序執行路中的調用其餘線程的thread1.join()方法,該程序須要等待thread1執行結束再繼續運行。java
線程讓步:yield,僅是將線程從執行狀態轉爲就緒狀態,不阻塞線程。在多cpu狀況下,這個效果根本沒用。因此yield也不是絕對能控制線程的方法apache
線程同步安全問題:安全
多個線程訪問同一資源,會形成線程錯誤: 由此衍生出幾種解決方案:session
1,synchronized:加鎖-修改-釋放鎖;多線程
2,lock ReentrantLock併發
ReentrantLock lock = new ReentrantLock(); lock.lock(); lock.unlock();
eg 銀行取款問題:ide
1,用戶輸入取款金額;函數
2,系統判斷帳號餘額是否大於取款金額;ui
3, 若是餘額大於取款金額,取款成功,若是餘額小於取款金額,取款失敗;this
銀行帳戶類 account.java
public class Account { private String accountNo; private double balance; public Account(String accountNo, double balance) { super(); this.accountNo = accountNo; this.balance = balance; } public String getAccountNo() { return accountNo; } public void setAccountNo(String accountNo) { this.accountNo = accountNo; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } }
取款線程類:
public class DrawThread extends Thread{ private Account account; private double drawAmount; public Account getAccount() { return account; } public void setAccount(Account account) { this.account = account; } public double getDrawAmount() { return drawAmount; } public void setDrawAmount(double drawAmount) { this.drawAmount = drawAmount; } public DrawThread(Account account, double drawAmount) { super(); this.account = account; this.drawAmount = drawAmount; } @Override public void run() { // TODO Auto-generated method stub super.run(); if(account.getBalance()>=drawAmount) { System.out.println("取錢成功!吐出鈔票"+drawAmount); // 修改餘額 account.setBalance(account.getBalance()-drawAmount); System.out.println("餘額:"+account.getBalance()); } else { System.out.println("餘額不足!"); } } public static void main(String[] args) { // TODO Auto-generated method stub } }
取款main:
import java.awt.List; import java.io.Console; import java.util.ArrayList; import java.util.LinkedList; import java.util.Stack; /** * */ /** * @author lrh * */ public class T { /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { // TODO Auto-generated method stub Account account = new Account("123", 1000); new DrawThread(account, 800).start(); new DrawThread(account, 800).start(); } }
多線程併發訪問,會形成取款錯誤:
取錢成功!吐出鈔票800.0 餘額:200.0 取錢成功!吐出鈔票800.0 餘額:-600.0
所以,引入同步代碼塊這個概念:
public class DrawThread extends Thread{ private Account account; private double drawAmount; public Account getAccount() { return account; } public void setAccount(Account account) { this.account = account; } public double getDrawAmount() { return drawAmount; } public void setDrawAmount(double drawAmount) { this.drawAmount = drawAmount; } public DrawThread(Account account, double drawAmount) { super(); this.account = account; this.drawAmount = drawAmount; } @Override public void run() { // TODO Auto-generated method stub super.run(); synchronized (account) { if(account.getBalance()>=drawAmount) { System.out.println("取錢成功!吐出鈔票"+drawAmount); // 修改餘額 account.setBalance(account.getBalance()-drawAmount); System.out.println("餘額:"+account.getBalance()); } else { System.out.println("餘額不足!"); } } } public static void main(String[] args) { // TODO Auto-generated method stub } }
synchronized 保證只有一個線程多共享資源account進行訪問
運行結果:
取錢成功!吐出鈔票800.0 餘額:200.0 餘額不足!
ThreadLocal:線程局部變量 ,爲每個使用該變量的線程都提供一個變量值得副本;
public class Account { private ThreadLocal<String> name = new ThreadLocal<>(); public Account(String Str) { super(); this.name.set(Str); System.out.println("----"+this.name.get()); } public String getName() { return name.get(); } public void setName(String str) { this.name.set(str); } }
public class MyTest extends Thread { private Account account; public MyTest(Account account,String name) { super(name); this.account=account; } public void run() { for(int i =0;i<10;i++) { if(i==6) { account.setName(getName()); } System.out.println(account.getName()+"帳號的i值:"+i); } } }
public class ThreadLocalTest { public static void main(String[] args) { // TODO Auto-generated method stub Account at = new Account("初始名"); new MyTest(at, "線程甲").start(); new MyTest(at, "線程乙").start(); } }
兩個線程各有一個線程局部變量.
result:
----初始名
null帳號的i值:0
null帳號的i值:1
null帳號的i值:0
null帳號的i值:1
null帳號的i值:2
null帳號的i值:2
null帳號的i值:3
null帳號的i值:4
null帳號的i值:5
null帳號的i值:3
null帳號的i值:4
null帳號的i值:5
線程甲帳號的i值:6
線程甲帳號的i值:7
線程甲帳號的i值:8
線程甲帳號的i值:9
線程乙帳號的i值:6
線程乙帳號的i值:7
線程乙帳號的i值:8
線程乙帳號的i值:9
線程同步鎖lock.
lock比synchroniezed方法和synchronized代碼塊更靈活,因此我的仍是喜歡用這個。
下面是一個本地例子:
import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.concurrent.locks.ReentrantLock; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class Test implements Runnable{ private final ReentrantLock lock = new ReentrantLock(); /* * 加鎖前打印 * */ public void run() { //加鎖前打印 System.out.println(new Date()+Thread.currentThread().getName() +" 準備進入線程"); lock.lock(); //鎖內延時 try { Thread.sleep(2222); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //鎖內打印 System.out.println(new Date()+Thread.currentThread().getName()+" 進入線程成功"); lock.unlock(); //解鎖後打印 System.out.println(new Date()+Thread.currentThread().getName()+" 離開線程成功"); } /** * @param args * @throws InterruptedException * @throws IOException * 建立兩個線程,並行執行 */ public static void main(String[] args) throws InterruptedException{ // TODO Auto-generated method stub Test t1= new Test(); new Thread(t1).start(); new Thread(t1).start(); //Thread.sleep(9999); } }
運行結果:
Sat May 06 17:14:31 CST 2017Thread-0 準備進入線程 Sat May 06 17:14:31 CST 2017Thread-1 準備進入線程 Sat May 06 17:14:33 CST 2017Thread-0 進入線程成功 Sat May 06 17:14:33 CST 2017Thread-0 離開線程成功 Sat May 06 17:14:35 CST 2017Thread-1 進入線程成功 Sat May 06 17:14:35 CST 2017Thread-1 離開線程成功
重點在時間,且兩者共用一個線程類
加鎖後1號線程沒法進入,被鎖在外頭了。這個控制的很好。
稍做修改:
import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.concurrent.locks.ReentrantLock; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class Test implements Runnable{ private final ReentrantLock lock = new ReentrantLock(); /* * 加鎖前打印 * */ public void run() { //加鎖前打印 System.out.println(new Date()+Thread.currentThread().getName() +" 準備進入線程"); lock.lock(); //鎖內打印 System.out.println(new Date()+Thread.currentThread().getName()+" 進入線程成功"); //鎖內延時 try { Thread.sleep(2222); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } lock.unlock(); //解鎖後打印 System.out.println(new Date()+Thread.currentThread().getName()+" 離開線程成功"); } /** * @param args * @throws InterruptedException * @throws IOException * 建立兩個線程,並行執行 */ public static void main(String[] args) throws InterruptedException{ // TODO Auto-generated method stub Test t1= new Test(); Test t2= new Test(); new Thread(t1).start(); new Thread(t2).start(); //Thread.sleep(9999); } }
重點在main函數裏面的:
Test t1= new Test();
Test t2= new Test();
new Thread(t1).start();
new Thread(t2).start();
運行結果其實就是兩個對象的兩把鎖了,這點尤爲要注意。
因此運行結果以下:
Sat May 06 17:17:39 CST 2017Thread-0 準備進入線程 Sat May 06 17:17:39 CST 2017Thread-0 進入線程成功 Sat May 06 17:17:39 CST 2017Thread-1 準備進入線程 --二者都進入鎖,由於實際上是兩把鎖 Sat May 06 17:17:39 CST 2017Thread-1 進入線程成功 Sat May 06 17:17:41 CST 2017Thread-0 離開線程成功 Sat May 06 17:17:41 CST 2017Thread-1 離開線程成功
線程通訊:使用BlockingQueue控制線程通訊