Java线程(二)
五 线程的睡眠sleep()
线程睡眠的原因:线程执行太快,或者需要强制进入下一轮。
实现:
public void run() {
for(int i = 0;i<5;i++){
// 很耗时的操作,用来减慢线程的执行
// for(long k= 0; k <100000000;k++);
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace(); .
}
System.out.println(this.getName()+" :"+i);
}
}
睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样可以保证该线程执行过程中会睡眠。
注意:
1、线程睡眠是帮助所有线程获得运行机会的最好方法。
2、线程睡眠到期自动恢复,并返回到准备就绪状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行,还要看是否抢到资源。
3、sleep()是静态方法,只能控制当前正在运行的线程。
六 优先级
与线程休眠类似,线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的并非没机会执行。
线程的优先级用1-10之间的整数表示,数值越大优先级越高,默认的优先级为5。
public static void main(String[] args) {
Thread t1 = new MyThread1();
Thread t2 = new Thread(new MyRunnable());
t1.setPriority(10); //设置优先级大小
t2.setPriority(1);
t2.start();
t1.start();
}
在一个线程中开启另外一个新线程,则新开线程称为该线程的子线程,子线程初始优先级与父线程相同。
七 让步和合并
让步:
线程让步的意思是让当时运行的让出upu资源给其他线程使用。
线程让步使用yield()方法,yield()为静态方法,功能是暂停当前正在执行的线程对象,并执行其他线程。
publicvoid run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程2第" + i + "次执行!");
Thread.yield();
}
}
合并:
线程合并:就是将几个并行线程的线程合并为一个单线程执行。
应用场景是当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法,其中join()非静态方法。
void join()
// 等待该线程终止。
void join(long millis)
// 等待该线程终止的时间最长为 millis毫秒。
void join(long millis,int nanos)
// 等待该线程终止的时间最长为 millis毫秒 + nanos 纳秒。
public static void main(String[] args) {
Thread t1 = new MyThread1();
t1.start();
for (int i = 0; i < 20; i++) {
System.out.println("主线程第" + i +"次执行!");
if (i > 2)
try {
//t1线程合并到主线程中,主线程停止执行过程,转而执行t1线程,直到t1执行完毕后继续。
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
八 守护线程
守护线程调用线程对象的方法是setDaemon(true),该方法必须在启动线程前调用。
public static void main(String[] args) {
Thread t1 = new MyCommon();
Thread t2 = new Thread(new MyDaemon());
t2.setDaemon(true); //设置为守护线程
t2.start();
t1.start();
}
九 并发协作
使用wait与notify、notifyAll等方法实现。
例子:“生产者-消费者-仓储”模型
1、生产者仅仅在仓储未满时候生产,仓满则停止生产。
2、消费者仅仅在仓储有产品时候才能消费,仓空则等待。
3、当消费者发现仓储没产品可消费时候会通知生产者生产。
4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费。
实现:
/**
* 仓库
*/
class Godown {
publicstaticfinalint max_size = 100;//最大库存量
publicint curnum; //当前库存量
Godown() {
}
Godown(int curnum) {
this.curnum = curnum;
}
/**
* 生产指定数量的产品
*
* @param neednum
*/
publicsynchronizedvoid produce(int neednum) {
//测试是否需要生产
while (neednum + curnum > max_size) {
System.out.println("要生产的产品数量" + neednum +"超过剩余库存量" + (max_size - curnum) +",暂时不能执行生产任务!");
try {
//当前的生产线程等待
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//满足生产条件,则进行生产,这里简单的更改当前库存量
curnum += neednum;
System.out.println("已经生产了" + neednum +"个产品,现仓储量为" + curnum);
//唤醒在此对象监视器上等待的所有线程
notifyAll();
}
/**
* 消费指定数量的产品
*
* @param neednum
*/
publicsynchronizedvoid consume(int neednum) {
//测试是否可消费
while (curnum < neednum) {
try {
//当前的生产线程等待
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//满足消费条件,则进行消费,这里简单的更改当前库存量
curnum -= neednum;
System.out.println("已经消费了" + neednum +"个产品,现仓储量为" + curnum);
//唤醒在此对象监视器上等待的所有线程
notifyAll();
}
}
/**
* 生产者
*/
class Producer extends Thread {
privateint neednum; //生产产品的数量
private Godown godown; //仓库
Producer(int neednum, Godown godown) {
this.neednum = neednum;
this.godown = godown;
}
publicvoid run() {
//生产指定数量的产品
godown.produce(neednum);
}
}
/**
* 消费者
*/
class Consumer extends Thread {
privateint neednum; //生产产品的数量
private Godown godown; //仓库
Consumer(int neednum, Godown godown) {
this.neednum = neednum;
this.godown = godown;
}
publicvoid run() {
//消费指定数量的产品
godown.consume(neednum);
}
}
说明:wait方法的作用是释放当前线程的所获得的锁,并调用对象的notifyAll()方法,通知(唤醒)该对象上其他等待线程(不释放锁,也不获取锁),使得其继续执行。这样,整个生产者、消费者线程得以正确的协作执行。
十 死锁
当两个线程被阻塞,每个线程在等待另一个线程时就发生死锁,一旦发生死锁,程序就死掉。
实现:
public class DeadlockRisk {
private static class Resource {
public int value;
}
private Resource resourceA =new Resource();
private Resource resourceB =new Resource();
publicint read() {
synchronized (resourceA) {
synchronized (resourceB) {
return resourceB.value + resourceA.value;
}
}
}
public void write(int a,int b) {
synchronized (resourceB) {
synchronized (resourceA) {
resourceA.value = a;
resourceB.value = b;
}
}
}
}
说明:假设read()方法由一个线程启动,write()方法由另外一个线程启动。读线程将拥有resourceA锁,写线程将拥有resourceB锁,两者都坚持等待的话就出现死锁