多線程-線程控制

 

// 經過實現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控制線程通訊

相關文章
相關標籤/搜索