synchronized:代碼開始上鎖,代碼結束時釋放鎖;內置鎖、自動化的、效率低、擴展性不高(不夠靈活); JDK1.5併發包Lock鎖 保證線程安全問題,屬於手動擋,手動開始上鎖,手動釋放鎖,靈活性高; Lock 介面與 synchronized 關鍵字的區別 Lock 介面可以嘗試非阻塞地獲取 ...
synchronized:代碼開始上鎖,代碼結束時釋放鎖;內置鎖、自動化的、效率低、擴展性不高(不夠靈活);
JDK1.5併發包Lock鎖 --保證線程安全問題,屬於手動擋,手動開始上鎖,手動釋放鎖,靈活性高;
Lock 介面與 synchronized 關鍵字的區別
Lock 介面可以嘗試非阻塞地獲取鎖 當前線程嘗試獲取鎖。如果這一時刻鎖沒有被其他線程獲取到,則成功獲取並持有鎖。
Lock 介面能被中斷地獲取鎖 與 synchronized 不同,獲取到鎖的線程能夠響應中斷,當獲取到的鎖的線程被中斷時,中斷異常將會被拋出,同時鎖會被釋放。
Lock 介面在指定的截止時間之前獲取鎖,如果截止時間到了依舊無法獲取鎖,則返回。
Lock用法:
Lock寫法
Lock lock = new ReentrantLock();
lock.lock();
try{
//可能會出現線程安全的操作
}finally{
//一定在finally中釋放鎖
//也不能把獲取鎖在try中進行,因為有可能在獲取鎖的時候拋出異常
lock.ublock();
}
class Res{
public String username;
public String sex;
//true 生產者等待,消費者可消費 false生產者可以生產,消費者不可消費
public boolean flag=false;
Lock lock=new ReentrantLock();
}
class Out extends Thread{
Res res;
Condition newCondition;
public Out(Res res,Condition newCondition){
this.res=res;
this.newCondition=newCondition;
}
@Override
public void run() {
//寫操作
int count=0;
while (true){
try{//防止異常後不釋放鎖
res.lock.lock();
if(res.flag){
newCondition.await();//讓當前線程從運行變為阻塞,並且釋放所的資源
}
if(count==0){//偶數
res.username="小明";
res.sex="男";
} else {//奇數
res.username="小紅";
res.sex="女";
}
count=(count+1)%2;
res.flag=true;
newCondition.signal();
} catch (Exception e){
} finally {
res.lock.unlock();
}
}
}
}
class Input extends Thread{
Res res;
Condition newCondition;
public Input(Res res,Condition newCondition){
this.res=res;
this.newCondition=newCondition;
}
@Override
public void run() {
while (true){
try{
res.lock.lock();
if(!res.flag){
newCondition.await();//讓當前線程從運行變為阻塞,並且釋放所的資源
}
System.out.println(res.username+","+res.sex);
res.flag=false;
newCondition.signal();
} catch (Exception e){
} finally {
res.lock.unlock();
}
}
}
}
public class LockDemo {
public static void main(String[] args) {
Res res = new Res();
Condition condition=res.lock.newCondition();
Out out = new Out(res,condition);
Input input = new Input(res,condition);
out.start();
input.start();
}
}
Condition用法:
Condition condition = lock.newCondition();
res. condition.await(); 類似wait
res. Condition. Signal() 類似notify
多線程併發(Thread)操作同一個資源---------網站併發(多個請求同時訪問一臺服務)
停止線程
停止線程思路
- 使用退出標誌,使線程正常退出,也就是當run方法完成後線程終止。
2. 使用stop方法強行終止線程;這個方法不推薦使用,因為stop和suspend、resume一樣,也可能發生不可預料的結果(沒執行完就終止沒有記錄上次執行點,並且不可恢復,不論你當前什麼狀況都給你停掉)。
3. 使用interrupt方法中斷線程。
那麼怎麼停止線程比較合適呢?或者說怎麼設計一種停止線程的方式?
class StopThreadDemo extends Thread{
private volatile boolean flag=true;
@Override
public void run() {
System.out.println("子線程開始執行......");
while (flag){
}
System.out.println("子線程執行結束......");
}
public void stopThread(){
this.flag=false;
}
}
public class StopThread {
public static void main(String[] args) throws InterruptedException {
StopThreadDemo stopThreadDemo=new StopThreadDemo();
stopThreadDemo.start();
for (int i = 0; i < 10; i++) {
System.out.println("我是主線程,i:"+i);
Thread.sleep(1000);
if(i==6)
stopThreadDemo.stopThread();
}
}
}
我是主線程,i:0
子線程開始執行......
我是主線程,i:1
我是主線程,i:2
我是主線程,i:3
我是主線程,i:4
我是主線程,i:5
我是主線程,i:6
我是主線程,i:7
子線程執行結束......
我是主線程,i:8
我是主線程,i:9
class StopThreadDemo extends Thread{
private volatile boolean flag=true;
@Override
public synchronized void run() {
System.out.println("子線程開始執行......");
while (flag){
try {
wait();
} catch (InterruptedException e) {
// e.printStackTrace();
stopThread();
}
}
System.out.println("子線程執行結束......");
}
public void stopThread(){
this.flag=false;
}
}
public class StopThread {
public static void main(String[] args) throws InterruptedException {
StopThreadDemo stopThreadDemo=new StopThreadDemo();
stopThreadDemo.start();
for (int i = 0; i < 10; i++) {
System.out.println("我是主線程,i:"+i);
Thread.sleep(1000);
if(i==6) {
//當前等待線程直接拋出異常
stopThreadDemo.interrupt();
// stopThreadDemo.stopThread();
}
}
}
}
以上兩種方式都可以。。。
可以看到這是一種停止線程的方式
ThreadLocal
ThreadLocal提高一個線程的局部變數,訪問某個線程擁有自己局部變數。
線程1和線程2各自操作自己的副本count,互相影響。
當使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的線程提供獨立的變數副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。
ThreadLocal的介面方法
ThreadLocal類介面很簡單,只有4個方法,我們先來瞭解一下:
void set(Object value)設置當前線程的線程局部變數的值。
public Object get()該方法返回當前線程所對應的線程局部變數。
public void remove()將當前線程局部變數的值刪除,目的是為了減少記憶體的占用,該方法是JDK 5.0新增的方法。需要指出的是,當線程結束後,對應該線程的局部變數將自動被垃圾回收,所以顯式調用該方法清除線程的局部變數並不是必須的操作,但它可以加快記憶體回收的速度。
protected Object initialValue()返回該線程局部變數的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,線上程第1次調用get()或set(Object)時才執行,並且僅執行1次。ThreadLocal中的預設實現直接返回一個null。
案例:創建三個線程,每個線程生成自己獨立序列號。
class ResNum{
public int count=0;
public static ThreadLocal
public String getNumber(){
count=threadLocal.get()+1;
threadLocal.set(count);
// count=count+1;
return count+"";
}
}
class LocalThreadDemo extends Thread{
private ResNum resNum;
public LocalThreadDemo(ResNum resNum){
this.resNum=resNum;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(getName()+","+resNum.getNumber());
}
}
}
public class ThreadLocalDemo {
public static void main(String[] args) {
ResNum resNum1=new ResNum();
// ResNum resNum2=new ResNum();
// ResNum resNum3=new ResNum();
LocalThreadDemo t1=new LocalThreadDemo(resNum1);
LocalThreadDemo t2=new LocalThreadDemo(resNum1);
LocalThreadDemo t3=new LocalThreadDemo(resNum1);
t1.start();
t2.start();
t3.start();
}
}