1.线程间通讯:
多个线程在处理同一资源,但是任务却不同。
2.等待唤醒机制:
涉及的方法:
1.wait() 让线程处于冻结状态,被wait的线程会被存储到线程池。通过锁来区分线程池(等待集)
2.notify() 唤醒线程池中的一个线程 (任意)。处于运行或者临时阻塞状态,即获得执行资格
3.notifyAll() 唤醒线程池中的所有线程。
这些方法必须定义在同步中,
因为这些方法是用于操作线程状态的方法,必须要明确到底操作的是哪个锁上的线程。
为什么操作线程的方法wait,notify,notifyall 定义在了object类中?
因为这些方法是监视器的方法。监视器就是锁。
锁可以是任意对象,任意对象调用的方法一定定义在object类中。
图解:
代码:
class Resource
{
String name;
String sex;
boolean flag = false;
}
class Input implements Runnable
{
private Resource r;
Input(Resource r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
synchronized(r)
{
x = ++x%2;
if(r.flag)
try
{
r.wait();
}
catch (Exception e)
{
}
if(x == 0)
{
r.name = "小美";
r.sex = "女";
}
else
{
r.name = "王五";
r.sex = "男";
}
r.flag = true;
r.notify();
}
}
}
}
class Output implements Runnable
{
private Resource r;
Output(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
synchronized(r)
{
if(!r.flag)
try
{
r.wait(); //说明是哪个锁调用wait()。对象锁也称对象监视器,而wait就是操作对象监视器上的线程。
}
catch (Exception e)
{
}
if(r!=null)
System.out.println(r.name + "....." + r.sex);
r.flag = false;
r.notify(); //说明是哪个锁调用notify()
}
}
}
}
class ResourceDemo
{
public static void main(String[] args)
{
Resource r = new Resource(); //c创建资源
Input in = new Input(r); //创建人物
Output out = new Output(r);
Thread t1 = new Thread(in); //创建线程,执行路径
Thread t2 = new Thread(out);
t1.start(); //开启线程
t2.start();
}
}
3.等待唤醒 代码 开发优化:
class Resource
{
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name, String sex) //已经解决name,sex同步问题
{
if(flag)
try
{
this.wait();
}
catch (Exception e)
{
}
this.name = name;
this.sex =sex;
flag = true;
notify();
}
public synchronized void out()
{
if(!flag)
try
{
wait();
}
catch (Exception e)
{
}
if(this!=null)
System.out.println(name + "....." + sex);
flag = false;
notify();
}
}
class Input implements Runnable
{
private Resource r;
Input(Resource r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
if(x == 0)
{
r.set("xiaoni","女");
}
else
{
r.set("王五","男");
}
x = ++x%2;
}
}
}
class Output implements Runnable
{
private Resource r;
Output(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class ResourceDemo
{
public static void main(String[] args)
{
Resource r = new Resource(); //c创建资源
Input in = new Input(r); //创建人物
Output out = new Output(r);
Thread t1 = new Thread(in); //创建线程,执行路径
Thread t2 = new Thread(out);
t1.start(); //开启线程
t2.start();
}
}
4.多生产者多消费者 问题:
两个关键点:
1.判断标记的循环:
应该用while,始终判断标记。但是可能会造成全部等待,产生死锁。
2.所以,为了避免死锁,应该使用全部唤醒notifyall(),这样可以唤醒对方线程,解决死锁问题。
if判断标记,只有一次,会导致不该运行的线程运行了。出现数据错误的情况。
while判断标记,解决了线程获取执行权后,是否要运行。
notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。
notifyAll:解决了,本方线程一定会唤醒对方线程的问题。但是全部唤醒效率低。
JDK1.5新工具解决办法:接口 Lock,接口Condition
接口 Lock:
它替代了同步代码块或者同步函数。将同步的隐式锁操作变成显示锁操作。同时更为灵活。可以一个锁上加上多组监视器。
lock() : 获取锁
unlock() : 释放锁,通常定义在finally代码块中。
Lock lock = new ReentrantLock(); //互斥锁
void show()
{
lock.lock();
try
{
code...
}
finally
{
lock.unlock(); //释放锁一定要做
}
}
接口Condition :
它替代了Object中的wait notify notifyAll方法。
将这些监视器方法单独进行了封装,变成Condition监视器对象。可以任意锁进行组合。
await();
signal();
signalAll();
API:
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
JDK1.5更新图解:
JDK1.5之前实现代码:
/*
生产者,消费者
多生产者,多消费者
*/
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name)
{
while(flag) //if(flag) //会发生死锁
//解决方案:全部唤醒, notifyAll();
try
{
wait(); //如果等待了,唤醒的时候从这里开始,不用进行前面的判断。所以有可能线程跳过了判断继续执行。
//解决方案将if 改成while,但可能导致四个线程全部等待,发生死锁
}
catch (InterruptedException e)
{
}
this.name = name + count;
System.out.println(Thread.currentThread().getName() + "生产了 " + this.name);
count++;
flag = true;
notifyAll();
}
public synchronized void out()
{
while(!flag) //if(!flag) //解决方案改成while
try
{
wait(); //如果等待了,唤醒的时候从这里开始,不用进行前面的判断。
}
catch (InterruptedException e)
{
}
System.out.println(Thread.currentThread().getName() +"吃掉了 " + name);
flag = false;
notifyAll();
}
}
class Producer implements Runnable
{
Resource r;
Producer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("烤鸭");
}
}
}
class Consumer implements Runnable
{
Resource r;
Consumer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class ProducerConsumerDemo
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer p = new Producer(r);
Consumer c = new Consumer(r);
Thread t1 = new Thread(p,"生产者1");
Thread t2 = new Thread(p,"生产者2");
Thread t3 = new Thread(c,"消费者1");
Thread t4 = new Thread(c,"消费者2");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
JDK1.5之 后 实现代码:
/*
生产者,消费者
多生产者,多消费者
jdk1.5后的解决方案:
*/
import java.util.concurrent.locks.*; //导入lock包
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
Lock lock =new ReentrantLock(); //创建一个锁对象
Condition producer_con = lock.newCondition(); //通过已有的锁,获得该锁上的监视器对象
Condition consumer_con = lock.newCondition();
public void set(String name)
{
lock.lock(); // 获取锁
try
{
while(flag) //if(flag) //会发生死锁
//解决方案:全部唤醒, notifyAll();
try
{
producer_con.await(); //如果等待了,唤醒的时候从这里开始,不用进行前面的判断。所以有可能线程跳过了判断继续执行。
//解决方案将if 改成while,但可能导致四个线程全部等待,发生死锁
}
catch (InterruptedException e)
{
}
this.name = name + count;
System.out.println(Thread.currentThread().getName() + "生产了 " + this.name);
count++;
flag = true;
consumer_con.signal();
}
finally
{
lock.unlock(); //释放锁
}
}
public void out()
{
lock.lock(); // 获取锁
try
{
while(!flag) //if(!flag) //解决方案改成while
try
{
consumer_con.await(); //如果等待了,唤醒的时候从这里开始,不用进行前面的判断。
}
catch (InterruptedException e)
{
}
System.out.println(Thread.currentThread().getName() +"吃掉了 " + name);
flag = false;
producer_con.signal();
}
finally
{
lock.unlock(); //释放锁
}
}
}
class Producer implements Runnable
{
Resource r;
Producer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("烤鸭");
}
}
}
class Consumer implements Runnable
{
Resource r;
Consumer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class ProducerConsumerDemo
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer p = new Producer(r);
Consumer c = new Consumer(r);
Thread t1 = new Thread(p,"生产者1");
Thread t2 = new Thread(p,"生产者2");
Thread t3 = new Thread(c,"消费者1");
Thread t4 = new Thread(c,"消费者2");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
API范例 补充:
import java.util.concurrent.locks.*;
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
System.out.println(Thread.currentThread().getName() + " produced " + putptr);
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
System.out.println(Thread.currentThread().getName() + " consum " + takeptr);
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
class Producer implements Runnable
{
BoundedBuffer b;
Producer(BoundedBuffer b)
{
this.b = b;
}
public void run()
{
while(true)
{
try
{
b.put(new Object());
}
catch (Exception e)
{
}
}
}
}
class Consumer implements Runnable
{
BoundedBuffer b;
Consumer(BoundedBuffer b)
{
this.b = b;
}
public void run()
{
while(true)
{
try
{
b.take();
}
catch (Exception e)
{
}
}
}
}
class ProducerConsumerAPIDemo
{
public static void main(String[] args)
{
BoundedBuffer boun = new BoundedBuffer();
Producer pro = new Producer(boun);
Consumer cons = new Consumer(boun);
Thread t1 = new Thread(pro,"Producer1");
Thread t2 = new Thread(pro,"Producer2");
Thread t3 = new Thread(cons,"Consumer1");
Thread t4 = new Thread(cons,"Consumer2");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
谁拿锁谁执行。
5.wait 和 sleep的区别:
1.wait可以指定时间也可以不指定。
sleep必须指定时间
2.在同步中时,对于CPU的执行权和锁的处理不同。
wait: 释放执行权,释放锁
sleep: 释放执行权,不释放锁
6.停止线程:
1.stop方法:过时
2.run方法结束:标记法
任务中都会有循环结构,只要控制住循环就可以结束任务。
控制循环通常就用定义标记来完成。
3.线程处于了冻结状态,无法读取标记。
public synchronized void run()
{
while(flag)
{
wait();
...
}
}
Thread方法:interrupt()中断线程。会抛出异常。
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格。强制动作会发生冻结状态异常InterruptedException,记得要处理。然后给标记赋值,让其强制读取。结束线程。
public synchronized void run()
{
while(flag)
{
try
{
wait();
}
catch(InterruptedException e)
{}
flag =flase;
}
}
t1.interrupt(); //强制唤醒
标记法示例:
class StopThread implements Runnable
{
private boolean flag = true;
public void run()
{
while(flag)
{
System.out.println(Thread.currentThread().getName()+".....");
}
}
public void setFlag()
{
flag = false;
}
}
class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 1;
for(;;)
{
if(++num==50)
{
st.setFlag();
break;
}
System.out.println("main......" + num);
}
System.out.println("over");
}
}
7.守护线程函数 setDaemon():将线程设置为后台线程(守护线程,用户线程等)。
前台线程必须手动结束。
当前台线程全部结束后,后台线程会自动结束。
t1.setDaemon(true);
当正在运行的线程都是守护线程时,Java 虚拟机退出。
8.其他方法:
1.join(): 等待该线程终止。 throws InterruptedException
先让该线程运行完到终止。也可以让冻结的线程强制恢复(interrupt()方法的功能)。
t1.join();//t1线程要申请加入进来,运行。这时主线程会将执行权释放出来,执行资格也释放,处于冻结状态。等t1结束后主线程再执行。
临时加入一个线程运算时可以使用join方法。
2.toString(): 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。 例:Thread[Thread-0,5,main]
优先级: 获取cpu执行权的几率,范围1—10。其中关键的几个优先级进行了字段封装:静态常量
MAX_PRIORITY 线程可以具有的最高优先级。10
MIN_PRIORITY 线程可以具有的最低优先级。1
NORM_PRIORITY 分配给线程的默认优先级。5
线程组:操作一个组的线程。方便。
3.setPriority():更改线程的优先级。
例: //t2.setPriority(10);
t2.setPriority(Thread.MAX_PRIORITY) //cpu会稍微优先照顾一下
4.Thread.yield():线程临时暂停,释放执行权。给其他线程机会,自己同时也有机会。
9.快速创建线程:
//线程的子类对象,匿名内部类
class ThreadTest
{
public static void main(String[] args)
{
new Thread() //线程一:直接创建Thread子类对象
{
public void run()
{
for(int x=0; x<50; x++)
{
System.out.println(Thread.currentThread().getName()+ "......x= " + x);
}
}
}.start();
new Thread(new Runnable() //线程二:创建Runnable接口的子类对象封装方法,把方法对象传给Theard线程对象
{
public void run()
{
for(int x=0; x<50; x++)
{
System.out.println(Thread.currentThread().getName()+ "...........y= " + x);
}
}
}).start();
for(int x=0; x<50; x++) //线程三:主函数
{
System.out.println(Thread.currentThread().getName()+ "...z= " + x);
}
}
}
10.多线程面试题:
1.class Test implements Runnable
{
public void run(Thread t) //这是子类的特有方法,并没有实现接口Runnable
的run()方法,所以该类要么是抽象类,要么就要覆盖run方法。显然没有定义抽象类,所以会报错。
{}
}
2.class ThreadTest
{
public static void main(String[] args)
{
new Thread //没有任务对象,以线程类本身为主
(
new Runnable() //没有子类,以任务对象为主
{
public void run()
{
System.out.println("runnable run");
}
}
)
{ //以子类为主
public void run()
{
System.out.println("subThread run");
}
}.start();
}
}