死锁:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不能正常运行。简单的说就是:线程死锁时,第一个线程等待第二个线程释放资源,而同时第二个线程又在等待第一个线程释放资源。 导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问
线程间通信:就是多个线程在线程在操作同一个资源,但是操作的动作不同
下面是两个线程间的通信的例子,如果不使用同步,则name和sex可能不同步:
class Res { //共享的资源
String name;
String sex;
}
class Input implements Runnable { //--------------使用1
private Res r;
Input(Res r) {
this.r = r;
}
public void run() {
int x = 0;
while (true) {
synchronized (Input.class) { // 而下面必须也是Input.class 或者是Output.class,总之上下两个线程锁必须一致才可以同步
if (x == 0) {
r.name = "mike";
r.sex = "man";
} else {
r.name = "丽丽";
r.sex = "女女女女";
}
x = (x + 1) % 2;
}
}
}
}
class Output implements Runnable {
private Res r;
Output(Res r) {
this.r = r;
}
public void run() {
while (true)
synchronized (Input.class) {
System.out.println(r.name + "----" + r.sex);
}
}
}
public class Test {
public static void main(String[] args) {
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in), t2 = new Thread(out);
t1.start();t2.start();
}
}
结果:丽丽----女女女女
wait(),notify(),notifyAll()
这三个方法用于协调多个线程对共享数据的存取,所以必须在Synchronized语句块内使用这三个方法。Synchronized用于保护共享数据,但是这样程序的流程就很不灵活了,此时用这三个方法来灵活控制。
线程可以调用对象的wait()方法使当前线程暂停执行并释放对象锁标志,进入等待状态,并且可以调用notify()或者notifyAll()方法通知正在等待的其他线程,然后等待同一个线程锁的notify,继续执行wait后未执行完的内容。notify()通知等待队列中的第一个线程,notifyAll()通知的是等待队列中的所有线程。
注意 这三个方法都是java.lang.Ojbect的方法! 为什么这些操作线程的方法要定义在Object中?因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒。不可以对不同锁中线程唤醒。也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义Object中
如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个。
把上面例子的输出改为交替输出:
class Res { // 共享的
String name;
String sex;
boolean flag = false;
}
class Input implements Runnable {
private Res r;
Input(Res r) {
this.r = r;
}
public void run() {
int x = 0;
while (true) {
synchronized (r) {
if (r.flag)
try { // --------接口exception不能拋
r.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (x == 0) {
r.name = "mike";
r.sex = "man";
} else {
r.name = "丽丽";
r.sex = "女女女女";
}
x = (x + 1) % 2;
r.flag = true;
r.notify();
}
}
}
}
class Output implements Runnable {
private Res r;
Output(Res r) {
this.r = r;
}
public void run() {
while (true)
synchronized (r) {
if (!r.flag)
try {
r.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(r.name + "----" + r.sex);
r.flag = false;
r.notify();
}
}
}
public class Test {
public static void main(String[] args) {
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in), t2 = new Thread(out);
t1.start();t2.start();
}
}
下面是生产者、消费者问题(多个生产者、消费者),每产生一个产品就消费一个
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name) {
while (flag)
try {
wait();
} catch (Exception e) {
}
this.name = name + "----" + count++;
System.out.println(Thread.currentThread().getName() + "----生产者----"+ this.name);
flag = true;
this.notifyAll();
}
public synchronized void out() {
while (!flag)
try {
wait();
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName()+ "----消费者---------" + this.name);
flag = false;
this.notifyAll();// 由notify换成notifyAll
}
}
class Producer implements Runnable {
private Resource res;
Producer(Resource res) {
this.res = res;
}
public void run() {
while (true)
res.set("+商品+");
}
}
class Consumer implements Runnable {
private Resource res;
Consumer(Resource res) {
this.res = res;
}
public void run() {
while (true)
res.out();
}
}
class Test {
public static void main(String[] args) {
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro),t2 = new Thread(pro),t3 = new Thread(con),t4 = new Thread(con);
t1.start();t2.start();t3.start();t4.start();
}
}
虽然 synchronized 方法和语句的范围机制使得使用监视器锁编程方便了很多,而且还帮助避免了很多涉及到锁的常见编程错误,但有时也需要以更为灵活的方式使用锁。jdk1.5中提供了多线程升级解决方案,将同步synchronized替换成现实Lock操作 ,将Object中的wait,notify,notifyAll替换成Condition对象, Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用
以下用lock实现生产者消费者
import java.util.concurrent.locks.*;
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();// //
private Condition condition_pro = lock.newCondition();// /
private Condition condition_con = lock.newCondition();// //
public void set(String name) throws InterruptedException {
lock.lock();// //
try {
while (flag)
condition_pro.await();// /
this.name = name + "----" + count++;
System.out.println(Thread.currentThread().getName() + "----生产者----"
+ this.name);
flag = true;
condition_con.signal();// //
} finally {
lock.unlock();// /
}
}
public void out() throws InterruptedException {
lock.lock();// /
try {
while (!flag)
condition_con.await();//
System.out.println(Thread.currentThread().getName()+ "----消费者---------" + this.name);
flag = false;
condition_pro.signal();
} finally {
lock.unlock();
}
}
}
class Producer implements Runnable {
private Resource res;
Producer(Resource res) {
this.res = res;
}
public void run() {
while (true)
try {
res.set("+商品+");
} catch (Exception e) {
}
}
}
class Consumer implements Runnable {
private Resource res;
Consumer(Resource res) {
this.res = res;
}
public void run() {
while (true)
try {
res.out();
} catch (Exception e) {
}
}
}
class Test {
public static void main(String[] args) {
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro),t2 = new Thread(pro),t3 = new Thread(con),t4 = new Thread(con);
t1.start();t2.start();t3.start();t4.start();
}
}
如何线程中断:定义循环结束标记,因为线程的代码一般都是循环,只要控制了循环即可; 特殊情况下,当冻结后,标记无法读取,使用interrupt()方法,结束线程的冻结状态,使线程回到运行状态来,但会产生InterruptedException异常
stop方法已经不再使用
interrupt(): 如果线程在调用 Object 类的 wait()、wait(long)或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。
class StopThread implements Runnable {
private boolean flag = true;
public synchronized void run() {
while (flag) {
try {
wait();
} catch (InterruptedException e) {
// 处理动结状态
System.out.println(Thread.currentThread().getName()+ "----Exception");
flag = false;
}
System.out.println(Thread.currentThread().getName() + "----run");
}
}
public void changeFlag() {
flag = false;
}
}
class Test {
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st),t2 = new Thread(st);
t1.start();t2.start();
int num = 0;
while (true) {
if (num++ == 60) {
// st.changeFlag();
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName() + "-----" + num);
}
System.out.println("over");
}
}
后台线程:setDaemon(boolean on)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,程序结束,Java 虚拟机退出。
yield() sleep() wait() 区别sleep()
使当前线程暂停执行一段时间,让其他线程有机会继续执行,只是轮到它执行时,它不执行run方法只是在那暂停了对cpu的调度没影响,但它并不释放对象锁。也就是如果 有Synchronized同步块,其他线程仍然不同访问共享数据。
join() t.join(),正在运行的线程在t线程运行结束后执行,也就是等待调用该方法的线程执行完毕后再往下继续执行,如果有多个线程,不影响其它线程。
yield() 与sleep()类似,只是不能由用户指定暂停多长时间
wait() wait()后,线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其它synchronized数据可被别的线程使用