线程间通信
其实就是多个线程在操作同一个资源,但是操作的动作不同。
线程间通信实例图
实例代码:
/*
* 需求:假设有一个数据库可以存放name和sex,一边在存数据,一边在取数据
*/
class Resources{
String name;
String sex;
}
//存数据
class Input implements Runnable{
Resources r;
int a=0;
Input(Resources r){
this.r = r;
}
public void run(){
while(!flag){
synchronized (r) {
if(a == 0){
r.name = "张三";
r.sex = "男";
}else{
r.name = "lihong";
r.sex = "woman";
}
a = (a+1)%2;
}
}
}
}
//取数据
class Output implements Runnable{
Resources r;
Output(Resources r){
this.r = r;
}
public void run(){
while(true){
synchronized (r) {
System.out.println("姓名:"+r.name + ",性别:" + r.sex);
}
}
}
}
public class InputOutputDemo {
public static void main(String[] args) {
Resources r = new Resources();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
等待唤醒机制
/*
* 需求:假设有一个数据库可以存放name和sex,一边在存数据,一边在取数据
*/
class Resources{
String name;
String sex;
private boolean flag = false;
public synchronized void set(String name, String sex){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.getMessage();
}
}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void get(){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.getMessage();
}
}
System.out.println("姓名:"+ name + ",性别:" + sex);
flag = false;
this.notify();
}
}
//存数据
class Input implements Runnable{
int a=0;
Resources r;
Input(Resources r){
this.r = r;
}
public void run(){
while(true){
if(a == 0){
r.set("张三", "男");
}else{
r.set("lihong", "woman");
}
a = (a+1)%2;
}
}
}
//取数据
class Output implements Runnable{
Resources r;
Output(Resources r){
this.r = r;
}
public void run(){
while(true){
r.get();
}
}
}
public class InputOutputDemo2 {
public static void main(String[] args) {
Resources r = new Resources();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
思考:
1.wait(),notify(),notifyAll()都使用在同步中?
因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。
2.wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
(1)这些方法存在与同步中。
(2)使用这些方法时必须要标识所属的同步的锁。只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。也就是说,等待和唤醒必须是同一个锁。
(3)锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
3.wait(),sleep()有什么区别?
wait():释放cpu执行权,释放锁。
sleep():释放cpu执行权,不释放锁。
生产者消费者实例-对于多个生产者和消费者
/*
* 生产者消费者实例
*/
class Resources{
private String name;
private int count = 0;
private boolean flag = false;
//生产
public synchronized void set(String name){
while(flag){
try {
this.wait();
} catch (InterruptedException e) {
}
}
this.name = name;
System.out.println("生产者生产了一个" + name+ ".....,编号:" + (++count));
flag = true;
this.notifyAll();
}
//消费
public synchronized void get(){
while(!flag){
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println("消费者吃了一个" + name+ ",编号:" + ++count);
flag = false;
this.notifyAll();
}
}
//生产者
class Producer implements Runnable{
Resources r;
Producer(Resources r){
this.r = r;
}
public void run(){
while(true){
r.set("蛋糕");
}
}
}
//消费者
class Consumer implements Runnable{
Resources r;
Consumer(Resources r){
this.r = r;
}
public void run(){
while(true){
r.get();
}
}
}
public class ProducerConsumerDemo {
public static void main(String[] args) {
//资源
Resources r = new Resources();
//创建两个生产者,并创建两个线程
new Thread(new Producer(r)).start();
new Thread(new Producer(r)).start();
//创建两个消费者,并创建两个线程
new Thread(new Consumer(r)).start();
new Thread(new Consumer(r)).start();
}
}
出现问题的解决方法:
1.定义while判断标记
2.把notify改成notifyAll
思考:
1.为什么要定义while判断标记。
原因:让被唤醒的线程再一次判断标记。
2.为什么定义notifyAll,
因为需要唤醒对方线程。因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。
3.只唤醒对方线程,该什么做呢?
JDK1.5中,提供了多线程升级解决方案Lock操作,通过创建多个condition对象,可以只唤醒对方线程
JDK1.5中提供了多线程升级解决方案
该升级解决方案,将同步synchrozed 替换成 显示 Lock 操作
将Object 中的 wait ,notify,notifyAll,替换成了condition 对象。
该对象可以Lock锁,进行获取。通过创建多个condition对象,可以唤醒指定线程。
生产者消费者实例(只唤醒对方线程)
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/*
* 生产者消费者实例(只唤醒对方线程)
*/
class Resources{
private String name;
private int count = 0;
private boolean flag = false;
ReentrantLock lock = new ReentrantLock();
Condition condition_pro = lock.newCondition();
Condition condition_con = lock.newCondition();
//生产
public void set(String name) throws InterruptedException{
//获取锁
lock.lock();
try {
while(flag){
condition_pro.await();
}
this.name = name;
System.out.println("生产者生产了一个" + name+ ".....,编号:" + (++count));
flag = true;
//唤醒消费者线程
condition_con.signal();
} finally{
//释放锁
lock.unlock();
}
}
//消费
public synchronized void get()throws InterruptedException{
//获取锁
lock.lock();
try {
while(!flag){
condition_con.await();
}
System.out.println("消费者吃了一个" + name+ ",编号:" + (++count));
flag = false;
//唤醒生产者线程
condition_pro.signal();
} finally{
//释放锁
lock.unlock();
}
}
}
//生产者
class Producer implements Runnable{
Resources r;
Producer(Resources r){
this.r = r;
}
public void run(){
while(true){
try {
r.set("蛋糕");
} catch (InterruptedException e) {
// TODO: handle exception
}
}
}
}
//消费者
class Consumer implements Runnable{
Resources r;
Consumer(Resources r){
this.r = r;
}
public void run(){
while(true){
try {
r.get();
} catch (InterruptedException e) {
// TODO: handle exception
}
}
}
}
public class ProducerConsumerDemo2 {
public static void main(String[] args) {
//资源
Resources r = new Resources();
//创建两个生产者,并创建两个线程
new Thread(new Producer(r)).start();
new Thread(new Producer(r)).start();
//创建两个消费者,并创建两个线程
new Thread(new Consumer(r)).start();
new Thread(new Consumer(r)).start();
}
}
停止线程
思考
如何停止线程?
stop方法已经过时。要停止线程只有一种,run方法结束。
run方法结束条件:
定义循环结束标记,因为线程运行代码一般都是循环,只要控制了循环即可。
特殊情况
当线程处于冻结状态,就不会读取到标记,那么线程就不会结束。这时可以使用interrupt(中断)方法。该方法是结束线程的冻结状态,使线程回到运行状态中来。
停止线程实例:
/*
* 停止线程实例
*/
class StopThread implements Runnable{
private boolean flag = true;
public int court = 0;
public synchronized void run() {
while(flag){
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+":Exception");
}
System.out.println(Thread.currentThread().getName()+":" + (++court));
}
}
public void changeFlag()
{
flag = false;
}
}
public class StopThreadDemo {
public static void main(String[] args) {
int num = 0;
StopThread st = new StopThread();
//创建两个线程
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
//启动线程
t1.start();
t2.start();
while(true){
if(num++ == 60)
{
st.changeFlag();
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+":"+num);
}
System.out.println("over");
}
}
线程类的其他方法
setDaemon(true)方法
1.守护线程(后台线程),线程对象.setDaemon(true),将该线程标记为守护线程或后台线程。
2.当正在运行的线程都是守护线程时,Java虚拟机退出,程序结束。
注意:
1.该方法必须在启动线程前调用。
2.一个线程被标示为守护线程,并不是他不运行了,他跟其他线程一样运行。只是当前台线程执行完后,这些线程就会跟着结束。
join方法
1.定义:等待该线程终止。
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
2.作用:
join可以用来临时加入线程执行。
setPriority方法
定义:设置线程的优先级。优先级范围1-10,默认为5.
yield方法
定义:暂停当前正在执行的线程对象,并执行其他线程。
注意:这个执行其他线程包括被暂停的线程,也就是说被暂停的线程还是有可能会被再次执行到,并不是说一定会执行其他线程而该线程在下一次就不会执行。