Java线程及多线程技术及应用(二)

5线程同步互斥

1、线程同步互斥的一个示例

多个线程同时访问或操作同一资源时,很容易出现数据前后不一致的问题。请看下面的例子:

男孩拿着折子去北京银行海淀分行取钱

女孩拿着男孩的银行卡去西单百货疯狂购物

男孩走到柜台钱询问帐户余额

银行的业务员小姐亲切地告诉他:"您还有10000元!"。

女孩看上了一件时髦的衣裳,准备买下

男孩在思考要取多少钱呢?

女孩到收银台准备刷卡消费

收银台刷卡机读取银行卡余额为10000元

女孩买衣服刷卡消费5000元

消费清单打印出来,消费:5000元 
余额:5000元

女孩离开商场

男孩思考了1毫秒

男孩决定取5000元

银行的业务员小姐为男孩办理相关业务手续

交易完成

银行的业务员小姐告诉男孩:"您的余额为5000元"。

男孩离开银行

男孩帐户中一共有10000元,男孩拿着存折从银行取走5000元,女孩拿着男孩的银行卡购物刷卡消费5000元,最后男孩的帐户里却还剩5000元。显然这是不正确的,但是为什么会发生这样的情况呢?我们可以这样分析:男孩可以看作是一条线程,女孩也可以看作是一条线程,在同一时刻,两个线程都操作了同一个资源,那就是男孩的帐户。男孩从查看帐户余额到取走现金应该被看作是个原子性操作,是不可再分的,然而当男孩查看完余额正思考取多少钱的时候,女孩购物消费了5000元,也就是说女孩这条线程打断了男孩这条线程所要执行的任务。所以男孩刚查看完的余额10000元就不正确了,最终导致帐户中少减了5000元。

为了避免这样的事情发生,我们要保证线程同步互斥,所谓同步互斥就是:并发执行的多个线程在某一时间内只允许一个线程在执行以访问共享数据

2、Java中线程互斥的实现机制

由多线程带来的性能改善是以可靠性为代价的,所以编程出线程安全的类代码是十分必要的。当多个线程可以访问共享资源(调用单个对象的属性和方法,对数据进行读、写、修改、删除等操作)时,应保证同时只有一个线程访问共享数据,Java对此提出了有效的解决方案—同步锁。任何线程要进入同步互斥方法(访问共享资源的方法或代码段)时,就必须得到这个共享资源对象的锁,线程进入同步互斥方法后其它线程则不能再进入同步互斥方法,直到拥有共享资源对象锁的线程执行完同步互斥方法释放了锁,下一个线程才能进入同步互斥方法被执行。

Java的这一线程互斥的实现机制可以用一个最通俗的比方来说明:比如公共卫生间就是一个共享资源,每个人都可以使用,但又不能同时使用,所以卫生间里有一把锁。一个人进去了,会把门锁上,其他人就不能进去。当Ta出来的时候,要打开锁,下一个人才能继续使用。

3、利用Synchronized关键字用于修饰同步互斥方法

(1)同步互斥方法

public synchronized void method(){

//允许访问控制的代码

}

(2)同步互斥代码块

synchronized(syncObject){

//允许访问控制的代码

}

(3)锁定整个类

publicsynchronized class SyncObject{

}

由于synchronized 块可以针对任意的代码块,且可任意指定上锁的对象,因此灵活性较高。但要注意:

l  synchronized可以用来限定一个方法或一小段语句或整个类(该类中的所有方法都是synchronized方法)

l  将访问共享数据的代码设计为synchronized方法

l  由于可以通过 private 关键字来保证数据对象只能被方法访问,所以只需针对方法提出一套同步锁定机制。通过synchronized 方法来控制对类中的成员变量(共享数据)的访问。

l  编写线程安全的代码会使系统的总体效率会降低,要适量使用

l  只有某一个线程的synchronized方法执行完后其它线程的synchronized方法才能被执行。

l  当前时间,只有一个线程访问被锁定的代码段,但不能保证其他线程去访问其他没有被锁定的代码段。因此所有对共享资源进行操作的代码段都应该加锁。

l  对数据库操作时,修改数据的线程要加锁,而读数据的线程可以不加锁

有了这种解决方案,我们用线程安全的代码来重新实现一下男孩和女孩取钱的故事。以下是核心代码:

package com.px1987.j2se.thread.synchronous.v2;
/** 帐户类 */
public class Account {
	/** 余额	 */
	private int balance;
	public Account(int balance) {
		this.balance = balance;
	}
}
package com.px1987.j2se.thread.synchronous.v2;
/** 男孩类,实现Runnable接口*/
public class Boy implements Runnable {
	/** 银行帐户*/
	Account account;
	public Boy(Account account) {

		this.account = account;
	}
	/** 男孩拿着折子去北京银行海淀分行取钱*/
	public void run() {
		System.out.println("男孩拿着折子去北京银行海淀分行取钱");
		synchronized (account) {
			System.out.println("男孩走到柜台钱询问帐户余额");
			int balance = account.getBalance();
			System.out.println("银行的业务员小姐亲切地告诉他:\"您还有" +
balance + "元!\"。");
			try {
				System.out.println("男孩在思考要取多少钱呢?");
				Thread.sleep(1);
				System.out.println("男孩思考了1毫秒");
			}
catch (InterruptedException e) {
				e.printStackTrace();
			}
			int money = 5000;
			System.out.println("男孩决定取" + money + "元");
			System.out.println("银行的业务员小姐为男孩办理相关业务手续");
			account.setBalance(balance - money);
			System.out.println("交易完成");
			System.out.println("银行的业务员小姐告诉男孩:\"您的余额为" +
account.getBalance()+ "元\"。");
		}
		System.out.println("男孩离开银行");
	}
}
package com.px1987.j2se.thread.synchronous.v2;
/** 女孩类,实现runnable接口*/
public class Girl implements Runnable {
	/** 女孩持有男孩的银行卡*/
	Account account;
	public Girl(Account account) {

		this.account = account;
	}
	/*** "女孩拿着小军的银行卡去西单百货疯狂购物*/
	public void run() {
		String tabs = "\t\t\t\t\t\t";
		System.out.println(tabs + "女孩拿着小军的银行卡去西单百货疯狂购物");
		System.out.println(tabs + "女孩看上了一件时髦的衣裳,准备买下");
		synchronized (account) {
			System.out.println(tabs + "女孩到收银台准备刷卡消费");
			int balance = account.getBalance();
			System.out.println(tabs + "收银台刷卡机读取银行卡余额为" + balance + "元");
			int payout = 5000;
			System.out.println(tabs + "女孩买衣服刷卡消费" + payout + "元");
			account.setBalance(balance - payout);
			System.out.println(tabs + "消费清单打印出来,消费:" + payout + "元" + "  余额:"
+ account.getBalance() + "元");
		}
		System.out.println(tabs + "女孩离开商场");
	}
}
package com.px1987.j2se.thread.synchronous.v2;
public class Bank {
	public static void main(String[] args) {
		Account account=new Account(10000);
		Thread boyThread=new Thread(new Boy(account));
		Thread girlThread=new Thread(new Girl(account));
		boyThread.start();
		girlThread.start();
	}
}

修改后的代码运行结果如下图:

男孩拿着折子去北京银行海淀分行取钱

女孩拿着小军的银行卡去西单百货疯狂购物

女孩看上了一件时髦的衣裳,准备买下

女孩到收银台准备刷卡消费

收银台刷卡机读取银行卡余额为10000元

女孩买衣服刷卡消费5000元

消费清单打印出来,消费:5000元 
余额:5000元

女孩离开商场

男孩走到柜台钱询问帐户余额

银行的业务员小姐亲切地告诉他:"您还有5000元!"。

男孩在思考要取多少钱呢?

男孩思考了1毫秒

男孩决定取5000元

银行的业务员小姐为男孩办理相关业务手续

交易完成

银行的业务员小姐告诉男孩:"您的余额为0元"。

男孩离开银行

从结果中可以看出来,男孩从查看余额到取钱,女孩没有操作帐户,所以最后的余额是正确的。

4、线程死锁

使用互斥锁容易产生死锁问题。比如:一个线程需要锁定两个对象才能完成,线程1拥有对象A的锁,线程1如果再拥有对象B的锁就能完成操作,线程2拥有对象B的锁,线程2如果再拥有对象A的锁就能完成操作。

很不幸的是线程1执行不下去了,因为线程1等待的资源对象B被线程2锁住了,线程2也执行不下去了,因为线程2等待的资源对象A被线程1锁住了,这样就造成了死锁。

阅读一段文字:由多线程带来的性能改善是以可靠性为代价的,主要是因为有可能产生线程死锁。死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不能正常运行。简单的说就是:线程死锁时,第一个线程等待第二个线程释放资源,而同时第二个线程又在等待第一个线程释放资源。这里举一个通俗的例子:如在人行道上两个人迎面相遇,为了给对方让道,两人同时向一侧迈出一步,双方无法通过,又同时向另一侧迈出一步,这样还是无法通过。假设这种情况一直持续下去,这样就会发生死锁现象。
    导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。“synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性访问权。当线程访问对象时,线程会给对象加锁,而这个锁导致其它也想访问同一对象的线程被阻塞,直至第一个线程释放它加在对象上的锁。

(1)死锁问题的一个代码示例

package com.px1987.j2se.thread.DeadLock;
class Thread1 implements Runnable {
	private Object a;
	private Object b;
	public Thread1(Object a, Object b) {
		super();
		this.a = a;
		this.b = b;
	}
	public void run() {
		synchronized (a) {
			System.out.println("Thread1获得对象a的锁");
			try {
				Thread.sleep(1);
			}
catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (b) {
				System.out.println("Thread1获得对象b的锁");
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
package com.px1987.j2se.thread.DeadLock;
class Thread2 implements Runnable {
	private Object a;
	private Object b;
	public Thread2(Object a, Object b) {
		super();
		this.a = a;
		this.b = b;
	}
	public void run() {
		synchronized (b) {
			System.out.println("Thread2获得对象b的锁");
			try {
				Thread.sleep(1);
			}
catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized (a) {
				System.out.println("Thread2获得对象a的锁");
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
package com.px1987.j2se.thread.DeadLock;
public class TestDeadLock {
	public static void main(String[] args) {
		Object a=new Object();
		Object b=new Object();
		Thread thread1=new Thread(new Thread1(a,b));
		Thread thread2=new Thread(new Thread2(a,b));
		thread1.start();
		thread2.start();

	}
}

(2)死锁问题的另一个代码示例

package com.px1987.j2se.thread.DeadLock;
public class ThreadDeadLock {
	public static void main(String[] args) {
		ThreadOne threadOne=new ThreadOne();
		ThreadTwo threadTwo=new ThreadTwo();
		String s1="s";
		String s2="sss";
		threadOne.op1=s1;
		threadTwo.op1=s1;
		threadOne.op2=s2;
		threadTwo.op2=s2;
		threadOne.start();
		threadTwo.start();
	}
}
class ThreadOne extends Thread{
	String op1;
	String op2;
	public void run(){// 同步中又有同步,就可能死锁
		synchronized(op1){
			System.out.println(Thread.currentThread().getName()+"锁定op1");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized(op2){
				System.out.println(Thread.currentThread().getName()+"锁定op2");
			}
		}
	}
}
class ThreadTwo extends Thread{
	String op1;
	String op2;
public void run(){
		synchronized(op2){
			System.out.println(Thread.currentThread().getName()+"锁定op2");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized(op1){
				System.out.println(Thread.currentThread().getName()+"锁定op1");
			}
		}
	}
}

6生产者消费者问题

1、生产者消费者问题的示例

生产者消费者问题也是一个典型的线程问题。我们举一个这方面的实例来说明:在一个果园里,有农夫和小孩,农夫会不停的采摘水果放入果园中心的一个水果筐直到水果筐满,而小孩会不停的从水果筐里拿水果来吃,直到水果拿完。分析这个模型我们可以看出:农夫可以看成是一个生产者的线程,小孩可以看成是一个消费者的线程,而大水果筐是共享资源。

2、用Java程序表述的代码示例

package com.px1987.j2se.thread.ProducerConsumer;
import java.util.Random;
/*** 水果类*/
public class Fruit {
	/*** 水果编号*/
	private int id;
	/*** 水果编号计数器*/
	private static int number = 0;
	/*** 水果品种 */
	private String variety;
	/*** 水果品种数组 */
	private String[] varietys = "苹果,桃子,梨子,香蕉,西瓜,荔枝,葡萄".split(",");
	public Fruit() {
		super();
		this.variety = varietys[new Random().nextInt(7)];
		this.id = ++number;
	}
}

水果筐应该设计成类似于栈的数据结构,其中包含一个数组来存放筐里的水果,而数组的下标就是水果筐的容量。设定一个索引index表示指向下一个将要放入水果的位置。类中的push方法模拟农夫向水果筐中放入水果,pop方法模拟小孩从水果筐中拿水果。这两个方法都要操作共享资源,所以push和pop方法都是同步互斥方法。

3、如何避免出现死锁

那同步的问题解决后是否会出现死锁呢?大家试想一下,如果生产的速度大于消费的速度就会导致功大于求,水果筐很容易就满了,然而生产者又一直抱着水果筐不放,没有机会给消费者使用,消费者不消费生产者就无法生产,所以就造成了死锁。

怎样解决呢?在两个同步互斥方法中用到了wait和notify方法,这两个方法是为了防止死锁的。

l  wait是Object类的方法,它的作用是拥有互斥锁的线程放弃锁的使用权,进入wait池进行等待,那么互斥锁就有可能被其他线程获得以执行其他任务。

l  notify也是Object类的方法,它的作用是从wait池中唤醒一条正在等待的线程进入就绪状态,被唤醒的这条线程就很可能重新获得cup和互斥锁来完成它的任务。

l  notifyAll和Notify很相似,它是从wait池中唤醒所有正在等待的线程进入就绪状态。

需要注意的是以上三个方法都只能在synchronized方法中应用,否者会出现下面的异常信息:IllegalMonitorStateException:current thread not owner。

4、实现的代码示例

package com.px1987.j2se.thread.ProducerConsumer;
import java.text.DecimalFormat;
import java.util.Arrays;
/*** 水果框类,类似一个栈的模型 */
public class FruitBasket {
	/*** 容量为10的水果数组,也就是说水果框最多能放下10个水果	 */
	private Fruit[] fruits = new Fruit[10];
	/*** 下一个将要放入水果的位置*/
	private int index = 0;
	/*** 水果框中是否为空 @return true为空,false为不空 */
	public boolean isEmpty() {
		return index == 0 ? true : false;
	}
	/*** 水果框是否装满* @return true为满,false为未满*/
	public boolean isFull() {
		return index == fruits.length ? true : false;
	}
	/*** 进栈方法,模拟农夫把水果放入筐中,@param name 农夫的名字,@param fruit 水果对象 */
	public synchronized void push(String name, Fruit fruit) {
		//用while循环,不用if,避免IndexOutOfBoundsException异常的产生
		while (isFull()) {
			//如果水果筐满了,需要等待
			try {
				this.wait();
			}
catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//将水果放入index指示的位置,index再上移一格
		fruits[index++] = fruit;
		System.out.println(name + " 向水果框中放入编号为" + fruit.getId() + "的"+
							fruit.getVariety());
		display();
		this.notify();	//通知其他等待的农夫或孩子可以开始工作啦
	}
	/*** 出栈方法,模拟小孩从水果筐中取出水果,@param name 小孩的名字,@return 取出的水果*/
	public synchronized Fruit pop(String name) {
		//用while循环,不用if,避免IndexOutOfBoundsException异常的产生
		while (isEmpty()) {
			try {	//如果水果筐空,需要等待
				this.wait();
			}
catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		Fruit fruit = null;
		fruit = fruits[--index];	//index下移一位,取出指示位置上的水果
		System.out.println(name + " 从水果框中拿出编号为" + fruit.getId() + "的"+
fruit.getVariety());
		display();
		this.notify();
		return fruit;
	}
	/*** 显示水果筐中水果存放情况*/
	public void display() {
		for (int i = 0; i < index; i++)
			System.out.printf("%-10s", " NO:" +
new DecimalFormat("00").format(fruits[i].getId())
								+ fruits[i].getVariety() + " |");
			for (int i = index; i < fruits.length; i++) {
				System.out.printf("%-10s", "   【" + (i + 1) + "】   |");
			}
			System.out.println();
		}
}
package com.px1987.j2se.thread.ProducerConsumer;
import java.util.Random;
/** 果园里的农夫类,他是生产者,实现Runnable接口*/
public class Farmer implements Runnable {
	/** 姓名*/
	private String name;
	/** 水果框*/
	private FruitBasket fruitBasket;
	/** 农夫会不停地重复这一系列动作:从水果树上采摘一个水果放入水果框中,然后随机的休息0-2秒*/
	public void run() {
		while (true) {
			fruitBasket.push(name, new Fruit());
			try {
				Thread.sleep(new Random().nextInt(2000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public Farmer(String name, FruitBasket fruitBasket) {
		super();
		this.name = name;
		this.fruitBasket = fruitBasket;
	}
}
package com.px1987.j2se.thread.ProducerConsumer;
import java.util.Random;
/*** 果园中的小孩类,他是消费者,实现Runnable接口*/
public class Child implements Runnable {
	/*** 姓名*/
	private String name;
	/*** 水果框*/
	private FruitBasket fruitBasket;
	/*** 小孩会不停地重复这一系列动作:从水果框中拿出水果吃,然后随机休息0-5秒钟*/
	public void run() {
		while (true) {
			fruitBasket.pop(name);
			try {
				Thread.sleep(new Random().nextInt(5000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public Child(String name, FruitBasket fruitBasket) {
		super();
		this.name = name;
		this.fruitBasket = fruitBasket;
	}
}
package com.px1987.j2se.thread.ProducerConsumer;
import java.util.Random;
/*** 果园中的小孩类,他是消费者,实现Runnable接口*/
public class Child implements Runnable {
	/*** 姓名*/
	private String name;
	/*** 水果框*/
	private FruitBasket fruitBasket;
	/*** 小孩会不停地重复这一系列动作:从水果框中拿出水果吃,然后随机休息0-5秒钟*/
	public void run() {
		while (true) {
			fruitBasket.pop(name);
			try {
				Thread.sleep(new Random().nextInt(5));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public Child(String name, FruitBasket fruitBasket) {
		super();
		this.name = name;
		this.fruitBasket = fruitBasket;
	}
}

测试时使用多个生产者线程和多个消费者线程

package com.px1987.j2se.thread.ProducerConsumer;
/** 果园类,测试*/
public class Orchard {
	public static void main(String[] args) {
		FruitBasket fruitBasket = new FruitBasket();
		Thread farmerThread1 = new Thread(new Farmer("农夫1", fruitBasket));
		Thread farmerThread2 = new Thread(new Farmer("农夫2", fruitBasket));
		Thread farmerThread3 = new Thread(new Farmer("农夫3", fruitBasket));
		Thread childThread1 = new Thread(new Child("小孩1", fruitBasket));
		Thread childThread2 = new Thread(new Child("小孩2", fruitBasket));
		Thread childThread3 = new Thread(new Child("小孩3", fruitBasket));
		farmerThread1.start();
		farmerThread2.start();
		farmerThread3.start();
		childThread1.start();
		childThread2.start();
		childThread3.start();
	}
}

程序的运行结果如下:

农夫1 向水果框中放入编号为1的苹果

NO:01苹果|   【2】   |   【3】   |   【4】   |   【5】   |   【6】   |

农夫3 向水果框中放入编号为2的荔枝

NO:01苹果 |NO:02荔枝 |  【3】   |   【4】   |   【5】   |   【6】   |

小孩2 从水果框中拿出编号为2的荔枝

NO:01苹果|   【2】   |   【3】   |   【4】   |   【5】   |   【6】   |

农夫2 向水果框中放入编号为3的香蕉

NO:01苹果 |NO:03香蕉 |  【3】   |   【4】   |   【5】   |   【6】   |

小孩1 从水果框中拿出编号为3的香蕉

NO:01苹果|   【2】   |   【3】   |   【4】   |   【5】   |   【6】   |

小孩3 从水果框中拿出编号为1的苹果

【1】   |   【2】   |   【3】   |   【4】   |   【5】   |   【6】   |

农夫2 向水果框中放入编号为4的苹果

NO:04苹果|   【2】   |   【3】   |   【4】   |   【5】   |   【6】   |

小孩1 从水果框中拿出编号为4的苹果

【1】   |   【2】   |   【3】   |   【4】   |   【5】   |   【6】   |

农夫1 向水果框中放入编号为5的苹果

NO:05苹果|   【2】   |   【3】   |   【4】   |   【5】   |   【6】   |

农夫3 向水果框中放入编号为6的西瓜

NO:05苹果 |NO:06西瓜 |  【3】   |   【4】   |   【5】   |   【6】   |

农夫2 向水果框中放入编号为7的苹果

NO:05苹果 |NO:06西瓜 | NO:07苹果|   【4】   |   【5】   |   【6】   |

小孩3 从水果框中拿出编号为7的苹果

NO:05苹果 |NO:06西瓜 |  【3】   |   【4】   |   【5】   |   【6】   |

小孩3 从水果框中拿出编号为6的西瓜

NO:05苹果|   【2】   |   【3】   |   【4】   |   【5】   |   【6】   |

小孩2 从水果框中拿出编号为5的苹果

【1】   |   【2】   |   【3】   |   【4】   |   【5】   |   【6】   |

农夫2 向水果框中放入编号为8的桃子

NO:08桃子|   【2】   |   【3】   |   【4】   |   【5】   |   【6】   |

农夫1 向水果框中放入编号为9的荔枝

NO:08桃子 |NO:09荔枝 |  【3】   |   【4】   |   【5】   |   【6】   |

农夫3 向水果框中放入编号为10的香蕉

NO:08桃子 |NO:09荔枝 | NO:10香蕉|   【4】   |   【5】   |   【6】   |

农夫1 向水果框中放入编号为11的桃子

NO:08桃子 |NO:09荔枝 | NO:10香蕉 |NO:11桃子 |  【5】   |   【6】   |

农夫1 向水果框中放入编号为12的荔枝

NO:08桃子 |NO:09荔枝 | NO:10香蕉 |NO:11桃子 | NO:12荔枝|   【6】   |

农夫3 向水果框中放入编号为13的西瓜

NO:08桃子 | NO:09荔枝 | NO:10香蕉 | NO:11桃子 | NO:12荔枝 | NO:13西瓜 |

小孩1 从水果框中拿出编号为13的西瓜

NO:08桃子 |NO:09荔枝 | NO:10香蕉 |NO:11桃子 | NO:12荔枝|   【6】   |

农夫2 向水果框中放入编号为14的西瓜

NO:08桃子 | NO:09荔枝 | NO:10香蕉 | NO:11桃子 | NO:12荔枝 | NO:14西瓜 |

时间: 2024-08-27 02:28:50

Java线程及多线程技术及应用(二)的相关文章

Java线程及多线程技术及应用

第6 章 Java线程及多线程技术及应用 6.1线程基本概念 1.进程和线程的基础知识 l 进程:运行中的应用程序称为进程,拥有系统资源(cpu.内存) l 线程:进程中的一段代码,一个进程中可以哦有多段代码.本身不拥有资源(共享所在进程的资源) 在java中,程序入口被自动创建为主线程,在主线程中可以创建多个子线程. 区别: 1.是否占有资源问题 2.创建或撤销一个进程所需要的开销比创建或撤销一个线程所需要的开销大. 3.进程为重量级组件,线程为轻量级组件 l 多进程: 在操作系统中能同时运行

Java线程及多线程技术及应用(一)

1线程基本概念 1.进程和线程的基础知识 进程:运行中的应用程序称为进程,拥有系统资源(cpu.内存) 线程:进程中的一段代码,一个进程中可以哦有多段代码.本身不拥有资源(共享所在进程的资源) 在java中,程序入口被自动创建为主线程,在主线程中可以创建多个子线程. 区别: 1.是否占有资源问题 2.创建或撤销一个进程所需要的开销比创建或撤销一个线程所需要的开销大. 3.进程为重量级组件,线程为轻量级组件 3.多进程: 在操作系统中能同时运行多个任务(程序) 4.多线程: 在同一应用程序中有多个

JAVA线程与多线程

去安卓面试的时候通常会问一些java问题,所以呢你可能觉得答问题时答案很蛋疼,今天来介绍一下线程. 先看几个概念: 线程:进程中负责程序执行的执行单元.一个进程中至少有一个线程. 多线程:解决多任务同时执行的需求,合理使用CPU资源.多线程的运行是根据CPU切换完成,如何切换由CPU决定,因此多线程运行具有不确定性. ● 线程 java中的线程 使用java.lang.Thread类或者java.lang.Runnable接口编写代码来定义.实例化和启动新线程. 一个Thread类实例只是一个对

Java中的多线程技术全面详解

本文主要从整体上介绍Java中的多线程技术,对于一些重要的基础概念会进行相对详细的介绍,若有叙述不清晰或是不正确的地方,希望大家指出,谢谢大家:) 为什么使用多线程 并发与并行 我们知道,在单核机器上,"多进程"并不是真正的多个进程在同时执行,而是通过CPU时间分片,操作系统快速在进程间切换而模拟出来的多进程.我们通常把这种情况成为并发,也就是多个进程的运行行为是"一并发生"的,但不是同时执行的,因为CPU核数的限制(PC和通用寄存器只有一套,严格来说在同一时刻只能

基于JAVA语言的多线程技术

1.简介 多线程技术属于操作系统范围内的知识: 进程与线程 可以这么理解,一个应用程序就是一个进程,在一个进程中包含至少一个线程:进程就是线程的容器,真正工作.处理任务的是线程. 进程是操作系统分配资源的基本单位:线程是操作系统进行调度,时间分配的基本单位: 进程由内核对象和地址空间两部分构成,内核对象就是一小块记录进程信息的内存,只允许操作系统访问:地址空间就是存放数据和程序的空间: 2.多线程运行机制 对于单个CPU,在每个时间点只能只能执行一个线程,多线程的实现是基于对时间片的轮回机制的,

Java线程与多线程教程

本文由 ImportNew - liken 翻译自 Journaldev. Java线程是执行某些任务的轻量级进程.Java通过Thread类提供多线程支持,应用可以创建并发执行的多个线程. 应用中有两类线程——用户线程和守护线程.当启动应用时,main线程是创建的第一个用户线程,我们可以创建多个用户线程和守护线程.当所有用户进程执行完毕时,JVM终止程序. 可以对不同的线程设置不同的属性,但并不保证高优先级的线程在低优先级线程之前执行.线程调度器是操作系统的部分实现,当一个线程启动后,它的执行

java线程跟多线程

java创建线程两种方式: 1.继承Thread创建线程 /** * Created by lsf on 16/4/18. */ class NewThread extends Thread { NewThread(){ super(); //创建线程 start(); //启动线程 } public void run() { long starttime = System.currentTimeMillis(); System.out.println("child thread..."

Java线程和多线程(十五)——线程的活性

当开发人员在应用中使用了并发来提升性能的同一时候.开发人员也须要注意线程之间有可能会相互堵塞. 当整个应用运行的速度比预期要慢的时候,也就是应用没有依照预期的运行时间运行完成.在本章中.我们来须要细致分析可能会影响应用多线程的活性问题. 死锁 死锁的概念在软件开发人员中已经广为熟知了,甚至普通的计算机用户也会常常使用这个概念.虽然不是在正确的状况下使用.严格来说,死锁意味着两个或者很多其它线程在等待还有一个线程释放其锁定的资源,而请求资源的线程本身也锁定了对方线程所请求的资源. 例如以下: Th

由浅入深理解Java线程池及线程池的如何使用

前言 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担.线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致Out of Memory.即便没有这样的情况,大量的线程回收也会给GC带来很大的压力. 为了避免重复的创建线程,线程池的出现可以让线程进行复用.通俗点讲,当有工作来,就会向线程池拿一个线程,当工作完成后,并不是直接关闭线程,而是将这个线程归还给线程池供其他任务使用. 接下来从总体到细致的方式,来共同探讨线程池. 总体的架构