2009年12月14日
#
線程同步指多個線程同時訪問某資源時,采用一系列的機制以保證同時最多只能一個線程訪問該資源。
為什么需要線程同步呢?
我們舉一個最簡單的例子來說明為什么需要線程同步。
比如有一本書(有且只有一本),交給多個售貨員同時去賣;
如果其中任何一個售貨員把這本書給賣了,其他售貨員就不能再賣這本書了。
現實生活中,如果要保證該書不會被多個售貨員同時賣掉,必須要有一種機制來保證:
比如,售貨員應該拿到該書之后才能開始賣書,暫時拿不到的話就只能等該書被退回柜臺。
售書的完整的例子可以參考
范例解說Java里的線程概念與線程同步技術
一文
這里,每一個售貨員售書可以看作一個線程。欲售的書便是各線程需要共享的資源。
開始售書之前,需要取得該書(資源),取不到情況下等待:
資源取得
開始售書之后,則需要取得對該書的獨享控制(不讓他人拿到該書):
資源加鎖
售完書時,需要通知柜臺該書已售出;或者未售出時,把書退回柜臺(通知他人可以拿到該書):
資源解鎖
synchronized控制線程同步的概念跟此完全一樣。
Java里可以使用synchronized來同步代碼塊或者方法。
同步代碼塊例:
- synchronized(欲同步的對象obj) {
- 需要同步的代碼塊
- }
synchronized(欲同步的對象obj) {
需要同步的代碼塊
}
可以同步代碼塊。
synchronized (obj)
表示若多個線程同時訪問時,只讓其中一個線程最先取得obj對象并對其加鎖,其它線程則阻塞直到取得obj對象的線程執行完代碼塊,此時被加鎖的obj對象得到釋放(解鎖),其它線程得到通知取得該book對象繼續執行。
很多情況下,可以使用synchronized
(this){...}來同步代碼塊。但需要注意的是,使用this作為同步對象的話,如果同一個類中存在多個synchronized
(this){...}代碼塊,其中任何一個synchronized(this)代碼塊處于被執行狀態,則其它線程對其他synchronized(this)代碼塊的訪問也會受到阻塞。
為了說明這個問題,我們舉例說明:

HelloSynchronized.java
- publicclass HelloSynchronized {
- publicstaticvoid main(String[] args) {
- //
- HelloSynchronized helloSynchronized = new HelloSynchronized();
- //創建2個線程t1, t2,分別調用HelloSynchronized helloSynchronized的2個方法method1,與method2
- Thread t1 = new Thread(new HelloSynchronizedRunnalbe(helloSynchronized, "method1"), "t1");
- Thread t2 = new Thread(new HelloSynchronizedRunnalbe(helloSynchronized, "method2"), "t2");
- t1.start();
- t2.start();
- }
- //synchronized public void method1() { //同步方法
- publicvoid method1() {
- synchronized (this) { //同步塊
- System.out.println(Thread.currentThread().getName()
- + " enter method1");
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- // do nothing
- }
- System.out.println(Thread.currentThread().getName()
- + " exit method1");
- }
- }
- //synchronized public void method2() { //同步方法
- publicvoid method2() {
- synchronized (this) { //同步塊
- System.out.println(Thread.currentThread().getName()
- + " enter method2");
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- // do nothing
- }
- System.out.println(Thread.currentThread().getName()
- + " exit method2");
- }
- }
- }
- class HelloSynchronizedRunnalbe implements Runnable {
- private HelloSynchronized helloSynchronized;
- private String methodName;
- public HelloSynchronizedRunnalbe(HelloSynchronized helloSynchronized, String methodName) {
- this.helloSynchronized = helloSynchronized;
- this.methodName = methodName;
- }
- publicvoid run() {
- if (methodName.equals("method1")) {
- helloSynchronized.method1();
- } elseif (methodName.equals("method2")) {
- helloSynchronized.method2();
- }
- }
- }
public class HelloSynchronized {
public static void main(String[] args) {
//
HelloSynchronized helloSynchronized = new HelloSynchronized();
//創建2個線程t1, t2,分別調用HelloSynchronized helloSynchronized的2個方法method1,與method2
Thread t1 = new Thread(new HelloSynchronizedRunnalbe(helloSynchronized, "method1"), "t1");
Thread t2 = new Thread(new HelloSynchronizedRunnalbe(helloSynchronized, "method2"), "t2");
t1.start();
t2.start();
}
//synchronized public void method1() { //同步方法
public void method1() {
synchronized (this) { //同步塊
System.out.println(Thread.currentThread().getName()
+ " enter method1");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// do nothing
}
System.out.println(Thread.currentThread().getName()
+ " exit method1");
}
}
//synchronized public void method2() { //同步方法
public void method2() {
synchronized (this) { //同步塊
System.out.println(Thread.currentThread().getName()
+ " enter method2");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// do nothing
}
System.out.println(Thread.currentThread().getName()
+ " exit method2");
}
}
}
class HelloSynchronizedRunnalbe implements Runnable {
private HelloSynchronized helloSynchronized;
private String methodName;
public HelloSynchronizedRunnalbe(HelloSynchronized helloSynchronized, String methodName) {
this.helloSynchronized = helloSynchronized;
this.methodName = methodName;
}
public void run() {
if (methodName.equals("method1")) {
helloSynchronized.method1();
} else if (methodName.equals("method2")) {
helloSynchronized.method2();
}
}
}
運行結果為:
t1 enter method1
t1 exit
method1
t2 enter method2
t2 exit
method2
等到線程t1結束后,t2才開始運行(t2受到阻塞)
再把synchronized
(this)去掉,運行結果為:
t1 enter method1
t2 enter
method2
t1 exit method1
t2 exit
method2
線程t1,t2同時運行
同步方法例:
- synchronizedprivatevoid sellBook(Book book) {
- ...
- }
synchronized private void sellBook(Book book) {
...
}
這種方法其實相當于
- privatevoid sellBook(Book book) {
- synchronized(this) {
- ...
- }
- }
private void sellBook(Book book) {
synchronized(this) {
...
}
}
由于默認采用this作為同步對象,所以當一個類中有多個synchronized方法時,同樣會存在以上問題:即如果有一個線程訪問其中某個synchronized方法時,直到該方法執行完畢,其它線程對其它synchronized方法的訪問也將受到阻塞。
大家可以把上面的例子稍加改造,去掉代碼中的synchronized
(this),改為synchronized public void method1(),synchronized public void
method2()同步形式,運行后會得到同樣結果。
多同步代碼塊synchronized(this){...}的多線程阻塞問題(包括synchronized同步方法),在并發處理的系統中(比如WEB服務器)會嚴重影響性能,建議慎重使用。可以使用synchronized(obj){...}縮小同步資源對象的范圍來解決這個問題。
線程 是一段完成某個特定功能的代碼,程序中的執行線程。Java
虛擬機允許應用程序并發地運行多個執行線程。
每個線程都有一個優先級,高優先級線程的執行優先于低優先級線程。
進程不同的是,由同名類生成的多個線程共享相同的內存空間和系統資源。
線程與進程的區別:
一個線程是一個程序內部的順序控制流。
1.
進程:每個進程都有獨立的代碼和數據空間(進程上下文)
,進程切換的開銷大。線程:同一類線程共享代碼和數據空間,每個線程有獨立的運行棧和程序計數器(PC),線程切換的開銷小。
2.
一個進程中可以包含多個線程。
本文將介紹以下線程方面的知識:
1,線程的創建
2,線程的狀態
3,線程同步
4,線程組
理解線程的最有效的方法是通過實例來理解。下面我們將通過
售貨員售書 為例,由淺入深地介紹線程的創建,通信,鎖機制等概念。
售貨員售書
我們假設一下售貨員售書的操作流程:
1,我們假設有20本書,交給2個售貨員去賣。
2,售貨員可以賣掉任何一本尚未賣出去的書。換句話說,同一本書若被其中一位售出去了,則不能被另外一位再售出了。
清單1:
文件名 | 說明 |
Book.java |
書籍類 |
SellBookRunnable.java |
售書類,線程的創建方法之一,該類實現了Runnable 接口,并實現了 run 方法。 |
SellBookThread.java |
售書類,線程的創建方法之一,該類聲明為 Thread 的子類,并重寫 Thread 類的 run 方法。 |
CallSellBook.java |
調用類。該類分別介紹了2種不同線程創建的調用方法。 |

Book.java
- publicclass Book {
- private String name;
- privateboolean sold = false;
- public Book(String name) {
- this.name = name;
- }
- public String getName() {
- return name;
- }
- publicvoid setName(String name) {
- this.name = name;
- }
- publicboolean isSold() {
- return sold;
- }
- publicvoid setSold(boolean sold) {
- this.sold = sold;
- }
- }
public class Book {
private String name;
private boolean sold = false;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isSold() {
return sold;
}
public void setSold(boolean sold) {
this.sold = sold;
}
}

SellBookRunnable.java
til.List;
- publicclass SellBookRunnable implements Runnable {
- private String saleMan;
- private List<Book> bookList;
- public SellBookRunnable(String saleMan, List<Book> bookList) {
- this.saleMan = saleMan;
- this.bookList = bookList;
- }
- publicvoid run() {
- for (int i = 0; i < bookList.size(); i++) {
- Book book = bookList.get(i);
- sellBook(book);
- }
- }
- /**
- * 售貨員賣書。我們這樣描述售貨員的賣書過程。
- *
- * @param book Book
- */
- privatevoid sellBook(Book book) {
- //從開始售書-到售書完成,使用synchronized (book)保證book不被其他售貨員售出
- synchronized (book) {
- if (book.isSold()) {
- return;
- } else {
- try {
- //為了讓各線程有執行機會,設置平均售書時間為0.5秒
- Thread.sleep(500);
- } catch (Exception e) {
- }
- //設置已售標志
- book.setSold(true);
- //打印該書已售信息
- System.out.println("[" + saleMan + "]" + book.getName() + " sold out:"
- + book.isSold() + ". by "
- + Thread.currentThread().getName());
- }
- }
- }
- }
import java.util.List;
public class SellBookRunnable implements Runnable {
private String saleMan;
private List<Book> bookList;
public SellBookRunnable(String saleMan, List<Book> bookList) {
this.saleMan = saleMan;
this.bookList = bookList;
}
public void run() {
for (int i = 0; i < bookList.size(); i++) {
Book book = bookList.get(i);
sellBook(book);
}
}
/**
* 售貨員賣書。我們這樣描述售貨員的賣書過程。
*
* @param book Book
*/
private void sellBook(Book book) {
//從開始售書-到售書完成,使用synchronized (book)保證book不被其他售貨員售出
synchronized (book) {
if (book.isSold()) {
return;
} else {
try {
//為了讓各線程有執行機會,設置平均售書時間為0.5秒
Thread.sleep(500);
} catch (Exception e) {
}
//設置已售標志
book.setSold(true);
//打印該書已售信息
System.out.println("[" + saleMan + "]" + book.getName() + " sold out:"
+ book.isSold() + ". by "
+ Thread.currentThread().getName());
}
}
}
}

SellBookThread.java
- import java.util.List;
- publicclass SellBookThread extends Thread {
- private String saleMan;
- private List<Book> bookList;
- public SellBookThread(String saleMan, List<Book> bookList) {
- this.saleMan = saleMan;
- this.bookList = bookList;
- }
- publicvoid run() {
- for (int i = 0; i < bookList.size(); i++) {
- Book book = bookList.get(i);
- sellBook(book);
- }
- }
- /**
- * 售貨員賣書。我們這樣描述售貨員的賣書過程。
- *
- * @param book Book
- */
- privatevoid sellBook(Book book) {
- //從開始售書-到售書完成,使用synchronized (book)保證book不被其他售貨員售出
- synchronized (book) {
- if (book.isSold()) {
- return;
- } else {
- try {
- //為了讓各線程有執行機會,設置平均售書時間為0.5秒
- Thread.sleep(500);
- } catch (Exception e) {
- }
- //設置已售標志
- book.setSold(true);
- //打印該書已售信息
- System.out.println("[" + saleMan + "]" + book.getName() + " sold out:"
- + book.isSold() + ". by "
- + Thread.currentThread().getName());
- }
- }
- }
- }
import java.util.List;
public class SellBookThread extends Thread {
private String saleMan;
private List<Book> bookList;
public SellBookThread(String saleMan, List<Book> bookList) {
this.saleMan = saleMan;
this.bookList = bookList;
}
public void run() {
for (int i = 0; i < bookList.size(); i++) {
Book book = bookList.get(i);
sellBook(book);
}
}
/**
* 售貨員賣書。我們這樣描述售貨員的賣書過程。
*
* @param book Book
*/
private void sellBook(Book book) {
//從開始售書-到售書完成,使用synchronized (book)保證book不被其他售貨員售出
synchronized (book) {
if (book.isSold()) {
return;
} else {
try {
//為了讓各線程有執行機會,設置平均售書時間為0.5秒
Thread.sleep(500);
} catch (Exception e) {
}
//設置已售標志
book.setSold(true);
//打印該書已售信息
System.out.println("[" + saleMan + "]" + book.getName() + " sold out:"
+ book.isSold() + ". by "
+ Thread.currentThread().getName());
}
}
}
}

CallSellBook.java
- import java.util.ArrayList;
- import java.util.List;
- //該類調用SellBookXxx類
- publicclass CallSellBook {
- /**
- * 用線程模擬這個售書的過程
- */
- publicstaticvoid main(String[] args) {
- //方法1:
- callSellBookThread();
- //or
- //方法2:
- //callSellBookRunnable();
- }
- //調用SellBookRunnable(Runnable接口實現類)模擬售書過程
- publicstaticvoid callSellBookThread() {
- List <Book>bookList = getBookListForSale();
- //將預售書籍清單交給售貨員SaleMan1
- Thread t1 = new SellBookThread("SaleMan1", bookList);
- //將預售書籍清單交給售貨員SaleMan2
- Thread t2 = new SellBookThread("SaleMan2", bookList);
- //售貨員SaleMan1開始售書
- t1.start();
- //售貨員SaleMan2開始售書
- t2.start();
- }
- //調用SellBookRunnable(Runnable接口實現類)模擬售書過程
- publicstaticvoid callSellBookRunnable() {
- List <Book>bookList = getBookListForSale();
- //將預售書籍清單交給售貨員SaleMan1
- Thread t1 = new Thread(new SellBookRunnable("SaleMan1", bookList));
- //將預售書籍清單交給售貨員SaleMan2
- Thread t2 = new Thread(new SellBookRunnable("SaleMan2", bookList));
- //售貨員SaleMan1開始售書
- t1.start();
- //售貨員SaleMan2開始售書
- t2.start();
- }
- //準備預售書籍
- publicstatic List<Book> getBookListForSale() {
- List <Book>bookList = new ArrayList();
- for (int i = 0; i < 20; i++) {
- Book book = new Book("Book" + i);
- bookList.add(book);
- }
- return bookList;
- }
- }
import java.util.ArrayList;
import java.util.List;
//該類調用SellBookXxx類
public class CallSellBook {
/**
* 用線程模擬這個售書的過程
*/
public static void main(String[] args) {
//方法1:
callSellBookThread();
//or
//方法2:
//callSellBookRunnable();
}
//調用SellBookRunnable(Runnable接口實現類)模擬售書過程
public static void callSellBookThread() {
List <Book>bookList = getBookListForSale();
//將預售書籍清單交給售貨員SaleMan1
Thread t1 = new SellBookThread("SaleMan1", bookList);
//將預售書籍清單交給售貨員SaleMan2
Thread t2 = new SellBookThread("SaleMan2", bookList);
//售貨員SaleMan1開始售書
t1.start();
//售貨員SaleMan2開始售書
t2.start();
}
//調用SellBookRunnable(Runnable接口實現類)模擬售書過程
public static void callSellBookRunnable() {
List <Book>bookList = getBookListForSale();
//將預售書籍清單交給售貨員SaleMan1
Thread t1 = new Thread(new SellBookRunnable("SaleMan1", bookList));
//將預售書籍清單交給售貨員SaleMan2
Thread t2 = new Thread(new SellBookRunnable("SaleMan2", bookList));
//售貨員SaleMan1開始售書
t1.start();
//售貨員SaleMan2開始售書
t2.start();
}
//準備預售書籍
public static List<Book> getBookListForSale() {
List <Book>bookList = new ArrayList();
for (int i = 0; i < 20; i++) {
Book book = new Book("Book" + i);
bookList.add(book);
}
return bookList;
}
}
執行CallSellBook
[SaleMan1]Book0 sold
out:true. by Thread-0
[SaleMan2]Book1 sold out:true. by
Thread-1
[SaleMan2]Book2 sold out:true. by Thread-1
[SaleMan2]Book3 sold
out:true. by Thread-1
[SaleMan2]Book4 sold out:true. by
Thread-1
[SaleMan2]Book5 sold out:true. by Thread-1
[SaleMan1]Book6 sold
out:true. by Thread-0
[SaleMan1]Book7 sold out:true. by
Thread-0
[SaleMan1]Book8 sold out:true. by Thread-0
[SaleMan1]Book9 sold
out:true. by Thread-0
[SaleMan1]Book10 sold out:true. by
Thread-0
[SaleMan1]Book11 sold out:true. by Thread-0
[SaleMan2]Book12 sold
out:true. by Thread-1
[SaleMan2]Book13 sold out:true. by
Thread-1
[SaleMan2]Book14 sold out:true. by Thread-1
[SaleMan2]Book15 sold
out:true. by Thread-1
[SaleMan2]Book16 sold out:true. by
Thread-1
[SaleMan2]Book17 sold out:true. by Thread-1
[SaleMan1]Book18 sold
out:true. by Thread-0
[SaleMan1]Book19 sold out:true. by Thread-0
線程的創建
創建新執行線程有兩種方法。
方法一種方法是將類聲明為 Thread
的子類。該子類應重寫 Thread 類的 run
方法。事實上類Thread本身也實現了接口Runnable,所以我們可以同過繼承Thread類實現線程體。
參考:
SellBookThread.java 與
CallSellBook.java
另一種方法是聲明實現 Runnable
接口的類。該類然后實現 run 方法。
參考:
SellBookRunnable.java 與
CallSellBook.java
線程的狀態
線程有四種狀態:創建狀態(New),可運行狀態(Runnable),阻塞狀態(Blocked),死亡狀態(Dead)。
創建狀態(New):
當執行完
Thread
t1 = new SellBookThread("SaleMan1",
bookList);
語句之后,則t1處于創建狀態(New)。此時t1并未真正運行。
可運行狀態(Runnable):
當Thread
t1被創建,并執行完
t1.start();
語句之后,t1就處于可運行狀態(Runnable)。此時,系統為線程t1分配其所需的系統資源。并對t1加以調用(或者根據任務調度情況準備調用)。
阻塞狀態(Blocked):
由于以下原因:
1)
調用了sleep()方法;
2) 調用了suspend()方法(該方法已不推薦使用);
3) 為等待條件鎖,調用wait()方法等;
4)
輸入輸出,或消息發生阻塞;
使得線程處于阻塞狀態(Blocked)。處于該狀態的線程即使處理器空閑,也不會得到執行。
死亡狀態(Dead):
死亡狀態(Dead)可以為自然死亡(線程運行完畢),或者調用了stop()方法(該方法已不推薦使用)。
線程的優先級:
可以通過Thread類的
void setPriority(int
newPriority)
方法為線程設置優先級。但是不能保證高優先級的線程就會被先運行。
線程組:
可以通過
ThreadGroup group = new
ThreadGroup(groupName);
Thread t1 = new Thread(ThreadGroup g, Runnable
r1);
Thread t1 = new Thread(ThreadGroup g, Runnable
r2);
等方法把多個線程加到一個線程組里去,這樣可以通過ThreadGroup對這些線程進行某些統一操作,
例如:group.interrupt();中斷該組所有線程。
線程unchecked異常處理器:
可以通過:
public void static
Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler)
方法為所有線程指定一個unchecked異常處理器,該處理器必須實現UncaughtExceptionHandler接口。
線程同步:
線程同步指多個線程同時訪問某資源時,采用一系列的機制以保證同時最多只能一個線程訪問該資源。
線程同步是多線程中必須考慮和解決的問題,因為很可能發生多個線程同時訪問(主要是寫操作)同一資源,如果不進行線程同步,很可能會引起數據混亂,造成線程死鎖等問題。
使用synchronized同步線程。
在J2SE5.0之前,只能使用synchronized來同步線程。可以使用synchronized來同步代碼塊或者方法。
同步代碼塊例:
synchronized(欲同步的對象obj)
{需要同步的代碼塊}可以同步代碼塊。
參考:
SellBookThread.java
- privatevoid sellBook(Book book) {
- synchronized (book) {
- ...
- }
- }
private void sellBook(Book book) {
synchronized (book) {
...
}
}
該例synchronized (book)
表示若多個線程同時訪問時,只讓其中一個線程最先取得book對象,其它線程則阻塞直到代碼塊執行完畢book對象被釋放后,其它線程才能取得該book對象繼續執行。
很多情況下,可以使用synchronized
(this){...}來同步代碼塊。但需要注意的是,使用this作為同步對象的話,如果同一個類中存在多個synchronized
(this){...}代碼塊,其中任何一個synchronized(this)代碼塊處于被執行狀態,則其它線程對其他synchronized(this)代碼塊的訪問也會受到阻塞。
同步方法例:
- synchronizedprivatevoid sellBook(Book book) {
- ...
- }
synchronized private void sellBook(Book book) {
...
}
這種方法其實相當于
- privatevoid sellBook(Book book) {
- synchronized(this) {
- ...
- }
- }
private void sellBook(Book book) {
synchronized(this) {
...
}
}
由于默認采用this作為同步對象,所以當一個類中有多個synchronized方法時,同樣會存在以上問題:即如果有一個線程訪問其中某個synchronized方法時,直到該方法執行完畢,其它線程對其它synchronized方法的訪問也將受到阻塞。
有關synchronized詳細說明我們將在其它文章中加以說明。
使用java.util.concurrent.locks.ReentrantLock和java.util.concurrent.locks.ReentrantReadWriteLock類同步線程。
J2SE5.0加入了ReentrantLock和ReentrantReadWriteLock可以對線程進行同步,這里舉一個最簡單的例子對其加以說明:
- class X {
- privatefinal ReentrantLock lock = new ReentrantLock();
- // ...
- publicvoid m() {
- lock.lock(); // block until condition holds
- try {
- // ... method body
- } finally {
- lock.unlock()
- }
- }
- }
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
其它J2SE5.0新導入的有關線程的相關接口/類:
java.util.concurrent.Future
Future接口可以保持/取得異步執行的結果值
java.util.concurrent.Callable
類似于Runnable接口。但Runnable不能返回值,也不能拋出checked異常
java.util.concurrent.ExecutorService
該接口繼承了Executor接口。可以通過submit方法把Runnable,Callable對象轉換為Future
形式。
java.util.concurrent.FutureTask
該類實現了Runnable和Future接口。提供異步執行的取消以及異步執行結果的取得等功能。
java.util.concurrent.Executor
執行指定的Runnable對象
java.util.concurrent.Executors
工具類。提供靜態方法可以創建Executor,ExecutorService,Callable等對象。可以通過newCachedThreadPool()等方法簡單創建線程池。
2009年12月1日
#
先看一段代碼
public class Hello{
public static void main(String[] args){
int i = 5 , j = 2;
System.out.println(i+j);
System.out.println(i-j);
System.out.println(i*j);
System.out.println(i/j);
}
}
編譯運行完上面的代碼后會得到各條指令運行的結果:7,3,10,2
這就是JAVA里面的加減乘除運算,為什么5除以2不等于2.5而等于2呢?這是因為這里做的是整數的四則運算,5除以2的結果是2,而余數為1,我們可以在上面的代碼上加上一條指令
System.out.println(i%j);
運行這個結果就會得到余數 1,這個 “%”執行的求余,或者叫取模。
想要得到5/2=2.5這個結果,我們需要改變對i和j的定義
把 int i = 5 , j=2; 改寫成double i =5 , j = 2;
再編譯運行一次,會得到如下的結果: 7.0 , 3.0 , 10.0 , 2.5.
可以看到結果都發生了變化,變成了帶小數點的,我們稱之為浮點型常量。
跟前面的例子一樣,我們也使用了這樣的定義方法
int i = 5,j = 2;
Scanner s = new Scanner(System.in);
前面說過,我們在這里是定義了一個Scanner類型的引用變量,它指向一個Scanner對象,但是這里跟以前有點不一樣(大概是java中僅有的幾處不一樣的地方了),我們定義了一個int型的變量i,然后把它的值賦成5(不是指向5),以后用到它的地方就相當于在用5做計算。
int是我們常用到八種基本數據類型之一,它表示整數型。
在JAVA中一共有八種基本數據類型,他們分別是
byte、short、int、long、float、double、char、boolean
整型
其中byte、short、int、long都是表示整數的,只不過他們的取值范圍不一樣
byte的取值范圍為-128~127,占用1個字節(-2的7次方到2的7次方-1)
short的取值范圍為-32768~32767,占用2個字節(-2的15次方到2的15次方-1)
int的取值范圍為(-2147483648~2147483647),占用4個字節(-2的31次方到2的31次方-1)
long的取值范圍為(-9223372036854774808~9223372036854774807),占用8個字節(-2的63次方到2的63次方-1)
可以看到byte和short的取值范圍比較小,而long的取值范圍太大,占用的空間多,基本上int可以滿足我們的日常的計算了,而且int也是使用的最多的整型類型了。
在通常情況下,如果JAVA中出現了一個整數數字比如35,那么這個數字就是int型的,如果我們希望它是byte型的,可以在數據后加上大寫的B:35B,表示它是byte型的,同樣的35S表示short型,35L表示long型的,表示int我們可以什么都不用加,但是如果要表示long型的,就一定要在數據后面加“L”。
浮點型
float和double是表示浮點型的數據類型,他們之間的區別在于他們的精確度不同
float 3.402823e+38 ~ 1.401298e-45(e+38表示是乘以10的38次方,同樣,e-45表示乘以10的負45次方)占用4個字節
double 1.797693e+308~ 4.9000000e-324 占用8個字節
double型比float型存儲范圍更大,精度更高,所以通常的浮點型的數據在不聲明的情況下都是double型的,如果要表示一個數據是float型的,可以在數據后面加上“F”。
浮點型的數據是不能完全精確的,所以有的時候在計算的時候可能會在小數點最后幾位出現浮動,這是正常的。
boolean型(布爾型)
這個類型只有兩個值,true和false(真和非真)
boolean t = true;
boolean f = false;
char型(文本型)
用于存放字符的數據類型,占用2個字節,采用unicode編碼,它的前128字節編碼與ASCII兼容
字符的存儲范圍在\u0000~\uFFFF,在定義字符型的數據時候要注意加' ',比如 '1'表示字符'1'而不是數值1,
char c = ' 1 ';
我們試著輸出c看看,System.out.println(c);結果就是1,而如果我們這樣輸出呢System.out.println(c+0);
結果卻變成了49。
如果我們這樣定義c看看
char c = ' \u0031 ';輸出的結果仍然是1,這是因為字符'1'對應著unicode編碼就是\u0031
char c1 = 'h',c2 = 'e',c3='l',c4='l',c5 = 'o';
System.out.print(c1);System.out.print(c2);System.out.print(c3);System.out.print(c4);Sytem.out.print(c5);
String
在前面我們看到過這樣的定義:
String s = "hello";
System.out.println(s);跟上面的5條語句組合起來的效果是一樣的,那么String是個什么呢?String是字符串,它不是基本數據類型,它是一個類,但是它又是一個有一點點特殊的類,它有很多性質和基本數據類型很像,以后我們會慢慢看到這些。
2009年10月10日
#
瘦客戶端(Thin Client):
指的是在客戶端-服務器網絡體系中的一個基本無需應用程序的計算機終端。
它通過一些協議和服務器通信,進而接入局域網。作為應用程序平臺的Internet的到來為企業應用程序提供了一個全新的領域:一個基于
Internet/intranet的應用程序運用一個只包含一個瀏覽器的瘦客戶端。這個瀏覽器負責解釋、顯示和處理應用程序的圖形用戶界面(GUI)和
它的數據。這樣的一個應用程序只需要被安裝在一個Web服務器上,用戶可以自動接收升級。一個解決方案只需要部署一次,甚至對成千的用戶也是如此,這種想
法的確很吸引人,尤其是Internet技術幫我們緩解了一些傳統的應用程序的障礙,比如防火墻和對多平臺的支持。
瘦客戶端將其鼠標、鍵盤等輸入傳送到服務器處理,服務器再把處理結果回傳至客戶端顯示。
不同的客戶端可以同時登錄到服務器上,模擬出一個相互獨立又在服務器上的工作環境。與此相反,普通客戶端會盡可能多地進行本地數據處理,與服務器(或其他
客戶端)的通信中只傳送必要的通信數據。
瘦客戶機具有IT高效性、安全性和經濟性
“胖客戶端”(Rich Client)是相對于“瘦客戶端”(Thin Client)(基于Web的應用程序)而言的,它是在客戶機器上安裝配置的一個功能豐富的交互式的用戶界面,例如Oracle、DB2數據庫的客戶端管理工具。
胖客戶端模式將應用程序處理分成了兩部分:由用戶的桌面計算機執行的處理和最適合一個集
中的服務器執行的處理。一個典型的胖客戶端包含一個或多個在用戶的PC上運行的應用程序,用戶可以查看并操作數據、處理一些或所有的業務規則——同時提供
一個豐富的用戶界面做出響應。服務器負責管理對數據的訪問并負責執行一些或所有的業務規則。這種模式也有一些“變種”,它們主要處理業務規則和數據的物理
位置。重點是,胖客戶端應用程序是在用戶的計算機上運行的。
九十年代末以來,基于Web的應用程序得到了廣泛的使用,這主要是因為它們可以很容易地
被終端用戶使用,終端用戶只要一臺能夠上網的電腦就行。然而,對于高交互性的程序接口來說,基于Web的接口很難滿足要求。編寫復雜的在終端用戶瀏覽器中
執行的客戶端腳本不是一個可行的增強交互性的方法。商業團體認識到有時候部署一個基于Web的解決方案并不能滿足所有用戶需求。此外,基于Web的應用程
序也不能夠脫機使用。
“富客戶端”(Rich Client)簡介富因特網應用程序(Rich
Internet
Applications,RIA)利用具有很強交互性的富客戶端技術來為用戶提供一個更高和更全方位的網絡體驗。RIA集成了桌面應用的交互性和傳統
Web應用的部署靈活性與成本分析,以創建單一而完整的用戶體驗。富客戶端技術使創建RIA成為可能,它提供一個運行時的環境以承載被編譯的客戶端應用程
序,該客戶端應用程序是一個使用HTTP協議發布的文件。客戶端應用程序使用異步的C/S結構連接到現有的應用服務器,這是一種安全的、可升級的、具有良
好適應性的面向服務模型,這種模型由當前所采用的Web服務驅動。
富客戶端技術正在不斷地完善中,但并不意味著會取代HTML。相反它將進一步擴展瀏覽器
功能,使之提供更加高效和友好的用戶接口。許多RIA都在瀏覽器中運行,甚至它本身就是HTML的一部分,所以HTML將繼續保持其原有的角色。另外,由
于富客戶端技術可以支持運動的圖象、視頻、音頻、雙向的數據通信和創建復雜的窗體,它為創建應用程序用戶接口提供了一個高效而完善的開發環境.
RIA開發必須具備三個要素:富客戶端技術、服務器技術和開發工具。富客戶端技術充分利
用本地機器的處理能力來處理數據,而不需要把某些數據發送到服務器處理,充分利用了本地機器的資源。服務器技術提供了一種與富客戶端的連接機制,作為
RIA的服務器技術必須從現有的服務器技術繼承,可以提供一個快速的腳本環境,支持數據庫應用開發、雙向數據通信、實時數據通信,甚至采用一種新的服務
器,例如:ColdFusion Server和Flash Communication
Server等。RIA實現必須有一組簡單而高效的開發工具,如果沒有一組簡單而高效的開發工具,那么富客戶端技術與服務器技術是毫無意義的。正是由于
RIA的C/S結構,它需要一組開發工具協同工作才可以完成。
2008年1月22日
#
1.下載ubuntu-7.10-alternate-amd64.iso(intel請下i386.iso)
然后放置在某個fat32分區的根目錄下,安裝的時候ubuntu會自動搜索到,這是我認為ubuntu一個很怪異地方——居然不需要指定路徑!Fedora可是半點都差不得路徑的。
(注意:下載 ubuntu-7.10-desktop-i386.iso 是沒用的,硬盤安裝找不到,不知道為什么;雖然google到了大俠的補救辦法,但是個人認為沒有必要舍近求遠)
2.下載initrd.gz和vmlinuz
把上述三個文件保存在fat32分區根目錄(當然最好是與ubuntu-7.10-alternate-amd64.iso同一個分區的根目錄下) 或者 ext3或reiserfs分區根目錄,最好不要保存在ntfs分區。
我保存在windows的C盤根目錄,在grub中表示為(hd0,0)。
3.下載 grub020p.zip ,網友修改過的,傳了上來,免得大家找得不對
(還是dos版本的grub好用,把安裝ubuntu的三種不同vga配置的grub的menu.lst都寫好了得,您可以拷貝并粘貼在C:\boot\grub\menu.lst的末尾處,安裝的時候直接按回車選擇就行了)
解壓到C:\下,編輯 boot.ini 文件,加入或者替換成如下代碼:
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS=”Microsoft Windows XP Professional” /noexecute=optin /fastdetect
C:\grldr=”GRUB For Dos/Windows ”
編輯C:\boot\grub\menu.lst 加入:
title Install-Ubuntu 791
kernel (hd0,0)/vmlinuz root=/dev/ram ramdisk_size=32000 devfs=mount,dall vga=791
initrd (hd0,0)/initrd.gz
boot
title Install-Ubuntu 792
kernel (hd0,0)/vmlinuz root=/dev/ram ramdisk_size=32000 devfs=mount,dall vga=792
initrd (hd0,0)/initrd.gz
boot
title Install-Ubuntu 771
kernel (hd0,0)/vmlinuz root=/dev/ram ramdisk_size=32000 devfs=mount,dall vga=771
initrd (hd0,0)/initrd.gz
boot
一共三種方式,這樣就不用再手寫了;防止寫錯,也省了麻煩。
注意:您可能需要修改(hd0,0)為其它方式,看你把initrd.gz和vmlinuz 放在哪個分區了,比如:
C盤是(hd0,0);
之后每一個邏輯盤,無論是linux的還是windows的都要占一個位置,比如:
D盤是(hd0,4);
E盤是(hd0,5);
F盤是(hd0,6);
….
比如D盤之前實際上還有一個linux分區,只不過Windows下看不見,那么Windows下的D盤實際上應該往后推一格,也就是是(hd0,5)了。
4.重新啟動電腦,選擇: GRUB For Dos/Windows
5.然后選擇: Install-Ubuntu 771 (這個隨便你來選擇,沒有辦法確定)
6.開始安裝根據安裝提示,設置語言、鍵盤、時區、網絡、分區、用戶名、密碼、grub等
需要說明的是,分區這一步很關鍵,我這種安裝方式沒有出現像Parition Magic那樣的圖形界面;而是文字界面讓我來配置和選擇。Ubuntu需要我們分配兩個linxu分區:一個ext3分區一個和swap分區。配置的時候,第一個ext3分區至少2G,這里mount掛載點應該要選擇為 “/”(root根目錄),第二個當然是swap分區,一般為內存大小的2倍,比如512M。同時每個分區最好選擇格式化,反正它是快速格式化,不費多少時間。
7。選擇是否安裝boot Manager(這一步我想應該是可選的)
安裝……安裝完以后,最后一步會問你檢測到了你的機子上有windows xp,為了更好的管理啟動選項,請問是否安裝boot Manager。我選擇了是,結果啟動引導選擇當然是沒問題了,但是先后出現了2重啟動菜單:第一次是GRUB的菜單,問你進WINDOWS還是 Ubuntu;如果你選進windows的話,那么剛才的boot.ini的多重選項還在,所以還要再選擇一次才能進入windows。當然進入 windows以后,你可以編輯boot.ini,去掉grub選項,因為現在已經不需要了。
還有是選擇不安裝boot Manager,這個我沒試,但根據壇子里的文章看,是可以的。應該是編輯一下剛才的C:\boot\grub\menu.lst文件,增加以下代碼:
title ubuntu
root (hd0,0)
kernel /vmlinuz root=/dev/hda5
initrd /initrd.img
8.自動重啟 (因為我們剛才在C:\boot\grub\menu.lst寫了boot,呵呵)
如果順利的話,一切就OK啦!!
需要特別指出的是,在中間安裝過程中,Ubuntu要求配置網絡。這一步完全可以省略掉,因為我這里網速不是很快,安裝之前我已經把網線拔掉了:)
最后的說明:
如果你只是淺嘗輒止,還是奉勸你不要嘗鮮,某些硬件廠商對linux支持不好(點名批評ATI)。我的x1200集成顯卡就是打死也開不了桌面3D特效。
最后衷心希望每個嘗試安裝ubuntu的朋友們安裝順利,我可是看著教程做都錯了兩三次,是不是太笨了點,朋友fxl也一起的,兩個男人一起做事就是不默契,fxl看了莫罵我哈:)感謝若干網友的友情帖子
轉載請注明——轉載自:Someus.cN[http://someus.cn]
2008年1月18日
#
摘要: 前言
Ubuntu是基于Debian的一個社區發行版,是主流發行版之一,現在有很多的linux愛好者使用這個易用、強大的發行版。此文面向對象是希望安裝配置好ubuntu的朋友,此文難度不大,按照本文安裝配置完成之后您可以繼續對您的系統進行更多的改進。
安裝ubuntu前,請備份好您的重要數據
附加篇:從舊版本升級到7.04方法(如果您已經安裝了7.04以前的版本,那么您可以不必重新安裝,...
閱讀全文
第一步:下載
1) 下載ubuntu正式版安裝iso鏡像(693兆):
http://releases.ubuntu.com/releases/.pool/ubuntu-7.10-alternate-i386.iso
2) 下載硬盤安裝引導文件和硬盤分區軟件(10.7兆):
http://hibaidu.cn/52abc/ubuntu/ubuntu.rar
推薦使用迅雷下載,下載后第一件事先檢校下載文件的md5的值,保證文件是正確的,然后才能進行下一步.
具體方法:解壓ubuntu.rar,里面有個md5.exe,打開,檢校文件值:
ubuntu 7.10系統安裝鏡像的md5是9A4AE3CFD68911A861D094EC834C9B48 (官方提供!)
我打包的ubuntu.rar的md5是E5BBF7A5EDBD5CA73CD10C2D3817EED5
md5不正確可能是別人動過手腳了,謹慎使用!!!

ubuntu-7.10-alternate-i386.iso下載后放置到C盤下備用,ubuntu.rar解壓后復制放置C盤下備用.
文件列表:

看到有個menu.lst文件沒有?如果你的內存不是512兆的你要改動一次:
menu.lst的內容是:
title Install Ubuntu //
kernel (hd0,0)/ubuntu/vmlinuz root=/dev/ram0 ramdisk_size=512000
initrd (hd0,0)/ubuntu/initrd.gz
看到上面的512000 沒有?內存是1G的就是改為1024000 ,內存是256的就是256000,以此類推.
文件放置位置示例:
C:\
|-ubuntu-7.10-alternate-i386.iso *系統安裝盤鏡像
|-menu.lst *系統引導啟動文件
|-grldr *grub for dos引導啟動文件
|--/ubuntu *安裝輔助文件夾
|-initrd.gz *安裝引導文件
|-vmlinuz *安裝引導文件
第二步分區:
安裝我打包文件:硬盤無損分區軟件PartitionMagicV8.05漢化版.exe
安裝好就打開文件開始分區,要從硬盤里面分出2塊空間來,大的一塊用作ubuntu的系統分區,小的用來做虛擬內存的交換區.
第一塊是EXT3格式,放置ubuntu系統用的,大小在5G以上,推薦10G,最好是15G以上,看你的情況.
圖示:
第二塊是SWAP格式,用作虛擬內存,大小是你內存的2倍左右,大了小了都不好.
圖示:
最后一步:
我的電腦點右鍵—>屬性->“高級”標簽->“啟動和故障恢復"欄—>設置->編輯系統啟動文件boot.ini,加入啟動項:
c:\grldr="Install ubuntu"
存盤退出.
示例圖:
這里說些重要事項:
1.下載后第一步就是檢校文件的md5,保證文件的正確性.為此我下載了2次,檢校md5是正確的:

2.硬盤分區時先退出除了分區軟件外的所有軟件,否則可能會因為人品問題導致分區失敗.
3.安裝過程要仔細看,最好有初三80分的英語水平,可能出現的對話框是E文的,看不懂可以查字典,慢慢來,不要慌.確實不理解里面的意思,先退出吧,把提示抄下來,弄清楚了再繼續.
4.安裝好了之后可能正常啟動后是黑屏,什么都沒有,你也不要慌,你先關機,拔掉電源再插上,系統有緩存的.多啟動2次都3次就可以了.還是不可以的話就在左下角選擇啟動會話是gnome,這樣應該就可以進入桌面了.
5.安裝后是多系統,之前的系統還可以用的,安裝不成功就是浪費了15G空間而已,你可以在Windows下合并分區.
為什么要看本文?網絡上不是有很多這樣的教程么?
理由1.新鮮,網絡上多是安裝ubuntu 7.04的教程,現在這里說的是ubuntu 7.10,但7.04的安裝過程還是值得借鑒的.
理由2.成功案例.本人反復安裝3次,硬盤安裝成功率100%.第一次成功安裝,但有些懷疑文件的md5,再換個鏡像下載,重新安裝,發現md5是正確的,也成功安裝了.ps:期間測試了一下刻盤鏡像能不能硬盤安裝,沒成功,屬于測試,不在討論范圍之內.
理由3.本文提供了ubuntu 7.10的引導安裝文件和分區軟件及使用方法,你只需要復制一下就可以了,不需要接觸命令行,不需要到處去下載文件,下載一個壓縮包就可以了,要用到的都為你準備好了.
好了,好的開端是成功的一半,我認為你做好上面的準備工作,你已經成功了90%了,祝你好運!