java能夠支持多線程,多線程是實現併發機制的一種有效手段,和進程一樣,線程也是實現併發的一個基本單位,但是線程是比進程更小的單位,線程是在進程的基礎上的進一步劃分。所謂所線程就是說一個進程在運行的過程中可以產生多個線程,這些線程可以同時存在,同時運行,一個進程可能包含有多個同時執行的線程。
首先,線程的實現:直接繼承Thread類和實現Runnable接口。這兩種方式都能夠實現多線程先給出例子:
繼承Thread類:
class ThreadT extends Thread{
public ThreadT(String name) {
super(name);
}
@Override
public void run() {
for(int i=0;i<20;i++)
System.out.println(this.getName()+"running"+i);
}
}
public class ThreadTest {
public static void main(String[] args) {
new ThreadT("Thread線程一").start();
new ThreadT("Thread線程二").start();
}
}
/**
* 結果:
*
* Thread線程一running0
Thread線程一running1
Thread線程一running2
Thread線程二running0
Thread線程一running3
Thread線程二running1
Thread線程一running4
Thread線程二running2
Thread線程一running5
Thread線程二running3
Thread線程二running4
Thread線程二running5
Thread線程二running6
Thread線程二running7
*/
啓動線程不能直接調用run()方法,必須調用start()方法,因爲線程的執行需要cpu的調度,同時每個線程都只能調用一次start()方法
實現Runnable接口的多線程:兩個線程一個打印“-”一個打印“|”同時打印:
public class ThreadTest01 {
public static void main(String[] args) {
// new Thread(new PrintHeng()).start();
// new Thread(new PrintShu()).start();
RunnableTest runnable1=new RunnableTest();
new Thread(runnable1,"thread1").start();
new Thread(runnable1,"thread2").start();
new Thread(runnable1,"thread3").start();
new Thread(runnable1,"thread4").start();
}
}
class PrintHeng implements Runnable{ // 打印橫線的線程
public void run() {
for(int i=0;i<20;i++)
{
System.out.print("-");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class PrintShu implements Runnable{ // 打印豎線的線程
public void run() {
for(int i=0;i<20;i++)
{
System.out.println("|");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
運行能同時打印橫線和豎線,但是注意到線程的start方法調用還是通過Thread類來實現的,這一點下面會給出解釋
既然通過Thread類和Runnable接口都可以實現多線程,那麼這兩種方法有什麼區別呢,首先給出Thread類的定義:
public class Thread implements Runnable {
//... 中間很多變量省略
//...
/* What will be run. */
private Runnable target; // 將目標線程作爲自己的參數
//... 中間部分省略
//...
public void run() {
if (target != null) { // 實際上就是調用了目標線程的run方法
target.run();
}
}
}
從定義中可以看出,Thread類也實現了Runnable 的接口,如果我們自己實現的線程也是實現Runnable接口方式的話就有了下面的關係:
可見當我們Thread類就相當於是我們目標線程(MyThread)的一個代理,通過target的start()方法來調用MyThread中的run方法,而不是直接運行我們Mythread中的run(),因爲直接調用run()的話並不需要cpu的調度相當於執行了一個普通方法。
除此之外兩種方式在共享變量時也是有所區別的,使用一個簡單的例子:
package edu.hue.jk.thread;
class MyThread extends Thread{
private int i=0;
public void run(){
System.out.println("Thread繼承方式:"+"----"+i++);
}
public MyThread() {
super();
}
}
public class ThreadTest02 {
public static void main(String[] args) {
for(int i=0;i<10;i++)
new Thread(new MyThread()).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
MyRunnable runnable=new MyRunnable();
for(int i=0;i<10;i++)
new Thread(runnable).start(); // 構造同一實例的多個線程
}
}
class MyRunnable implements Runnable{
private int i=0;
public void run() {
System.out.println("Runnable實現方式:"+"----"+i++);
}
public MyRunnable() {
super();
}
}
實驗結果:
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Runnable實現方式:----0
Runnable實現方式:----1
Runnable實現方式:----2
Runnable實現方式:----3
Runnable實現方式:----4
Runnable實現方式:----5
Runnable實現方式:----6
Runnable實現方式:----7
Runnable實現方式:----8
Runnable實現方式:----9
說明使用Runnable方式實現多線程時候,可以實現多個線程共享同一個實例(資源共享的效果)。
三個靜態方法用來控制進程的優先級:
除此之外還可以使用object中的三個方法來對進程進行調度:
this.notify(); // 喚醒另外一個進程
this.notifyAll(); // 喚醒所有進程
this.wait(); // 等待
this.wait(timeout); // 等待多長時間
使用進程同步:用一個例子來說明同步的重要性
package edu.hue.jk.thread;
//要理解進程同步首先要搞清楚進程同步的定義:
/**
* 在操作系統中進程同步是指,系統中多個進程中發生的事件存在某種時序關係,需要相互協作,共同完成任務。具體的說一個線程運行在某一點時
* 需要另外一個線程爲它提供消息,在未獲得消息之前一直處在等待狀態,獲得消息之後該進程被喚醒處於就緒狀態。而且當多個線程訪問同一份資源(貢獻資源)
* 的時候,可能會引起衝突,所以要加入同步機制來控制多個線程之間的訪問順序,一遍造成讀寫不一致的情況。
* 現在假設一個網上售票系統,有三個點可以售票,假設總共有五張票,程序如下:
* @author dell
*
*/
public class WhySynchronized {
/**
* @param args
*/
public static void main(String[] args) {
TiketThread tiket=new TiketThread();
new Thread(tiket,"tiket1").start();
new Thread(tiket,"tiket2").start();
new Thread(tiket,"tiket3").start();
}
}
class TiketThread implements Runnable{ // 充分說明了進程同步的重要性
static int tiket=5;
public void run() {
//synchronized(this){ // 不使用同步
while(true){
if(tiket>0)
{
try {
Thread.sleep(200); // 加入延時方便查看期待的結果
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"selling tikets"+tiket);
tiket--;
}
else
break;
}
//}
}
}
可見沒有使用同步,使得多線程的使用得不到我們需要的效果,將使用同步的註釋去掉就可以得到正確的結果。
爲了跟進一步的理解進程的同步使用方法,以及如何去操作進程,引入生產者和消費者的兩個例子:
<span style="color:#000000;">package edu.hue.jk.thread;
/**
* 用來描述生產者消費者問題 生產者不斷的生產產品,消費者不斷的取出產品;生產者生產一個產品,消費者就要取出一個產品,生產者不能連續生產,消費者也不能連續取產品
* 對於生產者和消費者來說,產品就是共享資源,而且不能同時去控制產品,所以使用同步操作
* 使用信號量來控制生產者和消費者的協調工作:
* s1=1 表示生產者可以生產,s1=0表示不能生產 初始值爲1
* s1=0表示消費者可以取產品 s1=1 表示不能取
**/
public class ProduceAndConsumer {
public static void main(String[] args) {
Product pro=new Product();
pro.setS1(1);
new Thread(new Produce(pro)).start();
new Thread(new Consumer(pro)).start(); // 這裏要怎樣修改
}
}
class Product{
private String productName;
int s1=1; // 用來協調生產者和消費者的信號量
public int getS1() {
return s1;
}
public void setS1(int s1) {
this.s1 = s1;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
}
class Produce implements Runnable{
Product product;
public Produce(Product product) {
super();
this.product = product;
}
public void run() {
while(true){ // 生產者不斷的生產產品
synchronized(product){ //使用同步塊
if(product.getS1()==1) // 能夠生產
{
product.setProductName("productName");
System.out.println("生產產品:"+product.getProductName());
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
product.setS1(0); // 生產者生產之後消費者能夠取出產品
product.notify(); // 喚醒消費者
}
else // 不能夠生產
{
System.out.println("不能生產產品:");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
product.wait(); // 等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
class Consumer implements Runnable{
Product product;
public Consumer(Product product) {
super();
this.product = product;
}
public void run() {
while(true){
synchronized(product){
if(product.getS1()==0)
{
System.out.println("消費者取出產品:"+product.getProductName());
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
product.setS1(1);
product.notify(); // 喚醒生產者
}
else
{
System.out.println("不能取出產品:");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
product.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}</span>
得到的結果
生產產品:productName
不能生產產品:
消費者取出產品:productName
不能取出產品:
生產產品:productName
不能生產產品:
消費者取出產品:productName
不能取出產品:
生產產品:productName
不能生產產品:
消費者取出產品:productName
不能取出產品:
生產產品:productName
生產者消費者問題變形:
<span style="color:#000000;">package edu.hue.jk.thread;
import java.util.LinkedList;
import java.util.List;
/**
*<span style="color:#cc0000;"> 生產者和消費者問題變形: 現在有一個容量固定的緩衝區,緩衝區滿時生產者停止生產,緩衝區空時消費者停止消費
* 前邊使用的是同步塊,下面使用同步方法來實現
* @author dell
</span> *
*/
public class ProduceAndConsumer02 {
public static void main(String[] args) {
ProductList list=new ProductList(5);
new Thread(new Produce02(list)).start();
new Thread(new Consumer02(list)).start();
}
}
class Product02 {
String productName;
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public Product02(String productName) {
super();
this.productName = productName;
}
}
// 表示緩衝區
class ProductList{
int length ;// 表示緩衝區的容量
int i=1; // 表示剷平的序號
List<Product02> productList;
public ProductList(int length) {
super();
this.length = length;
productList=new LinkedList<Product02>();
}
public ProductList() {
super();
productList=new LinkedList<Product02>();
}
// 使用同步方法生產產品
public synchronized void ProduceAProduct(){
while(true){
try {
Thread.sleep(300);
} catch (InterruptedException e1) { //爲了觀察效果而設置的延遲操作
e1.printStackTrace();
} //
if(length>productList.size()){ // 可以繼續生產產品
Product02 pro=new Product02("product"+i);
productList.add(pro); // 加入緩衝區
System.out.println("生產產品:"+pro.getProductName());
i++;
notify();
}
else{
try {
System.out.println("生產者等待。。。。。。");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public synchronized void getAProduct(){
while(true){
try {
Thread.sleep(300);
} catch (InterruptedException e1) {
e1.printStackTrace();
} //
if(productList.size()>0){ // 可以繼續生產產品
Product02 pro=productList.get(0); // 始終取第一個產品
productList.remove(0); // 從緩衝區取出
System.out.println("消費者取出:"+pro.getProductName());
notify();
}
else{
try {
System.out.println("消費者等待。。。。。。");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class Produce02 implements Runnable{
ProductList list;
public void run() {
list.ProduceAProduct();
}
public Produce02(ProductList list) {
super();
this.list = list;
}
}
class Consumer02 implements Runnable{
ProductList list;
public void run() {
list.getAProduct();
}
public Consumer02(ProductList list) {
super();
this.list = list;
}
}</span>
運行結果:
生產產品:product1
生產產品:product2
生產產品:product3
生產產品:product4
生產產品:product5
生產者等待。。。。。。
消費者取出:product1
消費者取出:product2
消費者取出:product3
消費者取出:product4
消費者取出:product5
消費者等待。。。。。。
生產產品:product6
生產產品:product7
生產產品:product8
生產產品:product9
生產產品:product10
生產者等待。。。。。。
消費者取出:product6