一、Java线程状态
start之后是进去就绪状态,等待CPU的调度,而不是立即进去运行状态。当run函数返回,线程就终止了。
二、Thread.sleep
谁调用Thread.sleep方法谁休息,自己进入阻塞状态。Thread.sleep方法属于线程自己控制自己的状态。如果线程在sleep之前拥有某个对象的锁,则它在sleep的时候还会抱着这把锁不放。
三、obj.interrupt
obj是线程对象,其它线程调用obj的interrupt方法,将会打断obj线程的睡眠,引发obj线程的异常。
obj.stop,其它线程调用obj的stop方法可以让obj线程直接退出,粗暴直接,不要轻易使用(可能会导致某些打开的资源来不及关闭线程就退出了)。
四、join合并线程
Thread t1=new Thread(Runnable r);
t1.start();
t1.join(); //当前线程阻塞在此处
正在执行的线程调用t1的join方法将会合并t1线程,即相当于当前线程在方法调用(当前线程阻塞),直到等到t1线程执行完毕,当前线程才会继续执行。
yield:当前在执行的线程让出一次CPU时间给其它线程执行,自己立即进入就绪状态,等待CPU的再次调用。
(Thread.yield()谁调用此方法谁就让出CPU时间,但并不能保证让出后其它线程可以抢占到CPU时间。)
threadObj.setDaemon()方法将该线程标记为守护线程或用户线程,当正在运行的线程都是守护线程时,JVM会退出(该方法必须在线程启动前调用)。
五、synchronized互斥锁
每个对象只有一把锁(每一个对象对应一把锁),多个线程获取同一把锁时才会产生互斥效果,多个线程获取不同的锁时不会产生互斥效果。synchronized互斥锁锁住的是对象而不是代码。
六、死锁
所谓死锁: 是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称程序处于死锁状态或程序产生了死锁,这些永远在互相等待的线程称为死锁线程。 由于资源占用是互斥的,当某个线程锁住某个资源后,使得有关线程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象:死锁。
假设有两个线程a和b,线程a和b结束的条件是分别同时锁住obj1和obj2对象,如果a已经获取到了obj1的锁正在努力获取obj2的锁但是obj2的锁已经被b获取,同时b也正在努力获取obj1的锁,因为只有线程结束才能释放锁,而正常结束的条件是一个线程必须同时获取到两个锁,因此a和b都得不到另外一个锁都无法结束,陷入了永久的等待状态,此时程序产生死锁。
死锁程序例如:
import java.util.logging.Level;
import java.util.logging.Logger;
public class DiedSynchronized {
public static void main(String[] args) {
Tellme m=new Tellme();
Thread t1=new Thread(m,"t1");
Thread t2=new Thread(m,"t2");
System.out.println(Thread.currentThread().getName()+":t1和t2线程已经启动……");
t1.start();
t2.start();
try {
Thread.sleep((long)10000);
} catch (InterruptedException ex) { }
if(t1.isAlive() && t2.isAlive()) {
System.out.println(Thread.currentThread().getName()+":10s内,t1和t2线程均没有正常结束,证明t1和t2线程死锁!");
System.exit(0);
}
else
System.out.println(Thread.currentThread().getName()+":t1和t2线程已经结束,证明没有死锁!");
}
}
class Tellme implements Runnable {
private Integer _number = new Integer(100);
public boolean _flag = true;
public void run() {
if(_flag) {
_flag=false;
synchronized(this) {
System.out.println(Thread.currentThread().getName()+":已经获取到this锁,正在获取Number锁……");
try {
Thread.sleep((long)1000);
} catch (InterruptedException ex) { }
synchronized(_number) {
System.out.println(Thread.currentThread().getName()+":this锁和Number锁同时获取完毕!");
}
}
}else {
synchronized(_number) {
System.out.println(Thread.currentThread().getName()+":已经获取到Number锁,正在获取this锁……");
try {
Thread.sleep((long)1000);
} catch (InterruptedException ex) { }
synchronized(this) {
System.out.println(Thread.currentThread().getName()+":this锁和Number锁同时获取完毕!");
}
}
}
}
}
run:
main:t1和t2线程已经启动……
t2:已经获取到Number锁,正在获取this锁……
t1:已经获取到this锁,正在获取Number锁……
main:10s内,t1和t2线程均没有正常结束,证明t1和t2线程死锁!
成功构建 (总时间: 10 秒)
t1和t2线程分别拥有this锁和Number锁,同时努力获取对方的锁,造成最终谁也得不到,程序进入死锁状态。Thread.sleep((long)1000)只是起放大作用,即使没有睡眠1秒程序也有可能进入死锁状态。
七、wait、notify、notifyAll
线程可以在任意对象的监视器(锁)上阻塞(wait,前提是获取到该对象的锁),也可以在唤醒任意一个wait在某个对象的监视器上的线程(notify,前提是获取到该对象的锁)。“获取到某个对象的锁”,就像获取到某种资格一样,只有有了这种资格才能够让自己阻塞在该锁上面或者唤醒已经阻塞在该锁上的其它线程。由此可知,每个对象的监视器上面自愿wait和被notify的线程只和该对象有关。因为每个对象都具有锁,每个锁均不同,故wait和notify的方法调用要通过对象调用,所以wait和notify方法要在Object中声明。
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Producer_Customer Program for testing wait_notify_notifyAll
*
* @author 忘川
*/
public class Producer_Customer {
public static void main(String[] args) {
Basket basket = new Basket();
Producer pro = new Producer(basket);
Customer cus = new Customer(basket);
Thread proth1 = new Thread(pro, "一");
Thread proth2 = new Thread(pro, "二");
Thread proth3 = new Thread(pro, "三");
Thread proth4 = new Thread(pro, "四");
Thread custh1 = new Thread(cus, "custh1");
Thread custh2 = new Thread(cus, "custh2");
proth1.start();
proth2.start();
proth3.start();
proth4.start();
custh1.start();
custh2.start();
}
}
class Producer implements Runnable {
private Basket basket = null;
public Producer(Basket basket) {
this.basket = basket;
}
public void run() {
Random r = new Random();
for (int i = 0; i < 4; i++) {
basket.put(new Apple(Thread.currentThread().getName() + "#" + i));
try {
Thread.sleep((long) r.nextDouble() * 1000);
} catch (InterruptedException ex) { }
}
}
}
class Customer implements Runnable {
private Basket basket = null;
public Customer(Basket basket) {
this.basket = basket;
}
public void run() {
Random r = new Random();
for (int i = 0; i < 8; i++) {
basket.eat();
try {
Thread.sleep((long) r.nextDouble() * 1000);
} catch (InterruptedException ex) { }
}
}
}
class Basket {
private Apple[] basket = new Apple[3];
private int index = 0;
public synchronized void put(Apple a) {
while (index == 2) {
try {
this.wait();
} catch (InterruptedException ex) { }
}
this.notifyAll();
basket[index++] = a;
System.out.println("生产:" + a + " 篮子剩余=" + index);
}
public synchronized void eat() {
while (index == 0) {
try {
this.wait();
} catch (InterruptedException ex) { }
}
this.notifyAll();
System.out.println("吃掉:" + basket[--index] + " 篮子剩余=" + index);
}
}
class Apple {
public String ID;
public Apple(String id) {
this.ID = id;
}
public String toString() {
return ID;
}
}
run:
生产:三#0 篮子剩余=1
生产:四#0 篮子剩余=2
生产:一#0 篮子剩余=3
吃掉:一#0 篮子剩余=2
生产:二#0 篮子剩余=3
吃掉:二#0 篮子剩余=2
生产:三#1 篮子剩余=3
吃掉:三#1 篮子剩余=2
生产:一#1 篮子剩余=3
吃掉:一#1 篮子剩余=2
生产:一#2 篮子剩余=3
吃掉:一#2 篮子剩余=2
吃掉:四#0 篮子剩余=1
生产:二#1 篮子剩余=2
生产:三#2 篮子剩余=3
吃掉:三#2 篮子剩余=2
生产:二#2 篮子剩余=3
吃掉:二#2 篮子剩余=2
生产:二#3 篮子剩余=3
吃掉:二#3 篮子剩余=2
生产:三#3 篮子剩余=3
吃掉:三#3 篮子剩余=2
吃掉:二#1 篮子剩余=1
生产:四#1 篮子剩余=2
生产:一#3 篮子剩余=3
吃掉:一#3 篮子剩余=2
生产:四#2 篮子剩余=3
吃掉:四#2 篮子剩余=2
生产:四#3 篮子剩余=3
吃掉:四#3 篮子剩余=2
吃掉:四#1 篮子剩余=1
吃掉:三#0 篮子剩余=0
成功构建 (总时间: 0 秒)
从运行结果可以看出,每当篮子中苹果个数达到3个(篮子所能容纳的上线时),生产者线程就自愿wait在篮子的监视器上,转而让消费者线程先消费之后再唤醒生产者线程;每当篮子中的苹果个数为0时,消费者线程就会自愿wait在篮子的监视器上,转而让生产者线程先生产之后再唤醒消费者线程。篮子就像一个控制中心,生产过剩,生产线程就资源wait,转而让消费线程消费;反之亦然。