2008年1月18日
#
线程同步指多个线程同时访问某资源时,采用一系列的机制以保证同时最多只能一个线程访问该资源。
为什么需要线程同步呢?
我们举一个最简单的例子来说明为什么需要线程同步。
比如有一本书(有且只有一本),交给多个售货员同时去卖;
如果其中任何一个售货员把这本书给卖了,其他售货员就不能再卖这本书了。
现实生活中,如果要保证该书不会被多个售货员同时卖掉,必须要有一种机制来保证:
比如,售货员应该拿到该书之后才能开始卖书,暂时拿不到的话就只能等该书被退回柜台。
售书的完整的例子可以参考
范例解说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()等方法简单创建线程池。
先看一段代码
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是字符串,它不是基本数据类型,它是一个类,但是它又是一个有一点点特殊的类,它有很多性质和基本数据类型很像,以后我们会慢慢看到这些。
瘦客户端(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结构,它需要一组开发工具协同工作才可以完成。
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]
摘要: 前言
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%了,祝你好运!