---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
进程:
是一个正在执行中的程序。
没一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者就一个控制单元。
一个进程中至少有一个线程。
线程:
就是进程中的一个独立的控制单元。线程在控制着进程的执行。
java JVM虚拟机启动时会有一个java.exe
该进程中至少一个线程负责java程序的执行。而且这个线程运行的代码存在与main方法中。
该线程称为--主线程,名称为:main。
扩展:
其实java 虚拟机JVM启动时是两个线程:一个是主线程,另一个垃圾回收机制的线程。
多线程的运行状态:
1,创建:线程被创建;
实例代码:
Thread thread = new Thread();
2,运行:通过start方法运行线程,有运行资格和运行权。
实例代码:
thread.start();
3,冻结:如果线程被sleep,或者wait,则进入冻结状态。放弃了运行权。
实例代码:
thread.sleep(12);或者是
thread.wait();
4,阻塞(临时)状态:线程只有执行资格,没有执行权。当线程被唤醒
或者sleep时间到的时候,可能会进入阻塞状态,获得执行资格,但没有执行权。
5,消亡:线程调用了stop方法,或者run方法执行完毕之后,线程结束。
注:stop方法已经过时,JDK1.5之后采用了新的停止机制!
多线程获取对象及名称:
static Thread currentThread()获得当前线程的对象。
String getName()获得线程的方法:
默认是:Thread + 编号,编号从0开始。
void setName()设置线程的名称。
线程的定义和启动
创建线程的第一种方式:
继承Thread类。继承方式
步骤:
1,定义类继承Thread。
2,重写Thread类的run方法。
目的:将自定义代码存储到run方法中,让线程运行。
3,调用线程的start方法,该方法两个作用:启动线程,调用run方法。
语法:class 类名 extends Thread{
@Override
public void run(){
//线程执行的代码
}
}
创建线程的第二种方式:实现方式
步骤:
1,定义类实现Runnable接口;
2,重写Runnable接口中的run方法;
作用:将线程要运行的代码存放到run方法中。
3,通过Thread类建立线程对象;
4,将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
重写run方法的目的:
因为:自定义的run方法所属的对象是Runnable接口的子类对象
所以要让线程去执行指定对象的run方法,就必须明确该run方法所属的对象。
5,调用Thread类的start方法开启线程,并调用Runnable接口接口子类的run方法。
语法:
class 类名 implements Runnable{
@Override
public void run(){
//线程执行的代码
}
}
实现方式和继承方式有什么区别?
实现方式,避免了java中单继承的局限性。
在定义线程时,建议使用实现方式。
两种方式的区别:
继承Thread类:线程代码存放在Thread类的run方法中,具有单重继承的局限性
实现Runnable,线程代码存放在Runnable接口的实现类run方法中,没有局限性,更加灵活
执行机制:
在多个线程启动后,每一次的运行结果都不同。因为对个线程都在获取CPU的执行权,
CPU执行到谁,谁就执行。需要明确的是:CPU在某一时刻,只能有一个程序在执行(多核除外)
CPU在做着快速切换,以达到看上去是同时运行的效果。
可以形象的把多线程的运行行为看成是在互相抢夺CPU的执行权。
这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多长时间,有CPU决定。
为什么要重写run方法?
Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码。
该功能就是run方法。即,Thread的类中的run方法,用于存储线程要运行的代码。
run方法和start方法的区别:
1,run方法时用于存储线程要执行的代码,
如果直接执行run方法,则此时是主线程在执行run方法。
此时是单线程,会先执行完run方法中的代码,才去执行其他的代码。
2,start方法是用于启动线程,并执行run方法。此时run方法就由start方法开启的线程执行。
此时就是主线程和start方法启动的线程在轮流执行。是多线程。
当线程启动之后,就不能再用同一线程重新启动, 会报IllegalThreadStateException非法线程状态异常。
自定义线程的示例代码:
package com.itheima.threaddemos;
/**
* 自定义线程类
* @author wuyong
*
*/
public class MyThreadTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建用方式一自定义线程
MyThread mt1 = new MyThread();
//创建方式二定义的线程
Thread mt2 = new Thread(new MyThread2());
//调用start方法,来启动线程,和调用run方法
mt1.start();
mt2.start();
for (int i = 0; i < 30; i++) {
System.out.println("系统线程 --" + i + "-- runnig ");
}
}
}
/**
* 自定义线程类
* 实现方式:继承Thread类
* @author wuyong
*
*/
class MyThread extends Thread{
/**
* 重写Thread类的run方法
*/
@Override
public void run() {
for (int i = 0; i < 30; i++) {
System.out.println("继承Thread类的自定义线程1 --" + i + "-- runnig ");
}
}
}
/**
* 自定义线程类
* 实现方式:实现Runnable接口。
* @author wuyong
*
*/
class MyThread2 implements Runnable{
/**
* 重写Thread类的run方法
*/
@Override
public void run() {
for (int i = 0; i < 30; i++) {
System.out.println("实现Runnable接口的自定义线程2 --" + i + "-- runnig ");
}
}
}
线程的等待和唤醒:
wait(),notify(),notifyAll()用来操作线程为什么定义在Object类中
1,这些方法存在于同步中。
2,使用这些方法时必须要标识所属的同步锁。
3,锁可以是任意对象,所有任意对象调用的方法一定是定义在Object中。
wait(),sleep()有什么区别?
wait():释放资源,释放锁。
sleep():释放资源,不释放锁。
wait();//使线程等待
notify();//唤醒线程,通常是唤醒线程池中的第一个被等待的线程。使用notify方法容易只唤醒本方线程,而导致所有线程都等待。
notifyAll();//唤醒线程池中的所有线程。
这些方法一般都使用在同步中,因为要对持有监视器(锁)的线程操作。所以都要使用在同步中,因为只有同步才具有锁。
为什么这些操作线程的方法都要定义在Object类汇总呢?
因为这些方法在操作同步线程时,都必须要标识他们所操作线程持有的锁,
只有同一个锁上的被等待线程,可以被同一个锁notify唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。
而锁可以是任意对象,所以任意对象调用的方法定义在Object类中。
注:当有多个线程同时操作资源时,会导致程序出现错误。因为没有判断标记。
要用while循环判断标记,和使用notifyAll方法唤醒等待中的线程来判断标记。
等待唤醒的示例代码:
package com.itheima.threaddemos.ThreadCommunication;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程间通信测试,及其wait,notify,notifyAll方法的测试类
* @author wuyong
*
*/
public class ThreadCommunicationTest2 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Resorce2 res2 = Resorce2.getInstance();
InputThread2 in = new InputThread2(res2);
OutputThread2 out = new OutputThread2(res2);
Thread t1 = new Thread(in);
Thread t2 = new Thread(in);
Thread t3 = new Thread(out);
Thread t4 = new Thread(out);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
/**
* 资源类
* @author wuyong
*
*/
class Resorce2{
private static final Resorce2 res2 = new Resorce2();
private Resorce2(){};
public static Resorce2 getInstance(){return res2;}
//姓名
private String name;
//国家
private String country;
//状态标示,用于控制线程是否等待或者唤醒
private boolean flag;
private Lock lock = new ReentrantLock();
private Condition con_pro = lock.newCondition();
private Condition con_cus = lock.newCondition();
/**
* 设置资源类的属性,并切换线程的等待或者唤醒,同步方法
* @param name
* @param country
*/
public synchronized void set(String name,String country) {
// if (flag) {
while (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.country = country;
System.out.println("现在出场的球星是---" + this.name);
flag = true;
// this.notify();
this.notifyAll();
}
public void set2(String name,String country) {
lock.lock();
try {
while (flag)
con_pro.await();
this.name = name;
this.country = country;
System.out.println("现在出场的球星是---" + this.name);
flag = true;
con_cus.signal();
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
/**
* 打印出资源的信息,同步方法
*/
public synchronized void out() {
while (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(this.name + "---" + this.country);
flag = false;
this.notifyAll();
}
public void out2() {
lock.lock();
try {
while (!flag)
con_cus.await();
System.out.println(this.name + "---" + this.country);
flag = false;
con_pro.signal();
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
}
/**
* 设置资源信息线程类
* @author wuyong
*
*/
class InputThread2 implements Runnable{
private Resorce2 res2;
public InputThread2(Resorce2 res2){
this.res2 = res2;
}
@Override
public void run() {
int i = 0;
while (true) {
if (i == 0)
res2.set2("kobe", "Amarican");
else
res2.set2("姚明", "中国");
i = (i + 1) % 2;
}
}
}
/**
* 输出资源信息线程类
* @author wuyong
*
*/
class OutputThread2 implements Runnable{
private Resorce2 res2;
public OutputThread2(Resorce2 res2){
this.res2 = res2;
}
@Override
public void run() {
while (true) {
res2.out2();
}
}
}
在JDK1.5中,新的等待唤醒机制:
将synchronized替换成了Lock接口操作。
将Object中的wait,notify,notifyAll,替换成了Condition对象,
该对象可以lock所,进行获取。
注:一个Lock对象可以有多个Condition实例,可以用对应的Condition实例,操作对应的线程。
其实:相当于就是用Lock代替了synchronized的操作,而Condition写的与是代替了Object中的wait,notify,notifyAll。
示例代码:
package com.itheima.threaddemos;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* JDK1.5之后的新的等待和唤醒机制
* @author wuyong
*
*/
public class Resource
{
private String name ;
private int count = 1 ;
private boolean flag = false ;
//线程锁
private Lock lock = new ReentrantLock();
//Condition实例对象,控制线程的等待和唤醒
private Condition condition_pro = lock. newCondition();
private Condition condition_con = lock. newCondition();
/**
* 生产
* @param name
* @throws InterruptedException
*/
public void produce( 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();//消费线程唤醒
condition_con .signalAll();//所有消费线程唤醒
}
finally
{
lock .unlock(); //释放锁的动作一定要执行。
}
}
/**
* 消费
* @throws InterruptedException
*/
public void consume() throws InterruptedException
{
lock .lock();//获取锁
try
{
while(!flag )
condition_con .await();//消费线程等待
System .out. println(Thread .currentThread().getName()+"...消费者........." +this.name);
flag = false ;
condition_pro .signal();//生产线程唤醒
condition_pro .signalAll();//所有生产线程唤醒
}
finally
{
lock .unlock();//释放锁
}
}
}
线程的停止:
由于Thread类中的stop方法已经过时,stop方法偶安全问题,所有不建议使用stop方法来停止。
如何停止线程呢?
只有一种方法,就是让run方法结束,开启多线程是,运行代码通常使用循环结构,所以控制了循环,就可以使run方法结束,即线程结束。
特殊情况:
当线程处于了冻结状态:如:wait,sleep,join。
就不会读取到标记。那么线程就不会结束。
解决方案:强制清除线程的冻结状态,获得执行资格。
当没有指定的方式让冻结的线程恢复到运行状态时,这时就需要对冻结进行清除。
强制让线程恢复到运行状态中来,这样就可以操作标记,让线程结束。
而Thread类提供了该方法 interrupt();interrupt()方法时强制将处于冻结状态下的线程恢复到运行状态。
结束线程的实例代码:
package com.itheima.threaddemos.ThreadCommunication;
/**
* 线程停止测试类
* 条件:必须是多线程下,的同步代码。
* 由于Thread类中的stop方法已经过时,
* 如何停止线程呢?
* 只有一种方法,就是让run方法结束,
* 开启多线程是,运行代码通常使用循环结构,所以控制了循环,就可以使run方法结束,即线程结束。
*
* 特殊情况:
* 当线程处于了冻结状态:如:wait,sleep,join。
* 就不会读取到标记。那么线程就不会结束。
*
* 当没有指定的方式让冻结的线程恢复到运行状态时,这时就需要对冻结进行清除。
* 强制让线程恢复到运行状态中来,这样就可以操作标记,让线程结束。
* 而Thread类提供了该方法 interrupt();
* @author wuyong
*
*/
public class ThreadStopTest {
/**
* @param args
*/
public static void main(String[] args) {
MyStopThread mst = new MyStopThread();
Thread t1= new Thread(mst);
Thread t2 = new Thread(mst);
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
int i = 0;
while(true){
if (i == 60) {
// 强制将冻结状态下的线程恢复到运行状态,使线程对标记进行判断,从而到达控制循环,以结束run方法。
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName() + "---run");
i++;
}
}
}
/**
* 自定义线程类
* @author wuyong
*
*/
class MyStopThread implements Runnable{
//控制线程状态的标记
boolean flag = true;
@Override
public synchronized void run() {
while (flag) {
try {
this.wait();//线程等待(冻结状态)
} catch (InterruptedException e) {
flag = false;
System.out.println(Thread.currentThread().getName() + "stop");
}
System.out.println(Thread.currentThread().getName() + "...run");
}
}
}
多线程的安全问题。
问题原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,
另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中其他线程不能参与执行。
java对于线程安全问题提供了专业的解决方式。
方式一:同步代码块
synchronized(对象)//这称为锁(同步锁)。必须要是同一对象。持有锁的线程才可以在同步中执行,
//没有锁的线程即使获得CPU执行权也无法执行。例如火车的卫生间。
{
//需要被同步的代码
}
方式二:同步方法。
访问修饰符 sychronized 返回值类型 方法名(参数列表){
//方法体
}
注:同步函数中,使用的锁是this。即函数所属对象的引用。
而静态的同步方法,使用的锁是该方法所属在类的字节码文件对象。
因为:
静态进入内存时,内存中还没有本类对象,但是一定有该类对应的字节码文件对象。
类名.class 该对象的类型是Class。
静态同步方法的语法格式:
访问修饰符 static sychronized 返回值类型 方法名(方法所属类名.class){
//方法体
}
如何确定同步那些地方:
1,明确那些代码是对线程运行代码。
2,明确那些是共享数据。(一般是成员变量)
3,明确多线程运行代码中哪些语句是操作共享数据的。
同步的前提:
1,要有多个线程。2,多个线程共同使用一个所,就是共享同一资源。
3,同步中只能有一个线程在执行。
同步的好处:解决了多线程的安全问题。
弊端:多个线程执行时,每次都要对所进行判断,较为消耗资源。
多线程同步的示例代码:
package com.itheima.threaddemos.synchronizeds;
/**
* 同步代码块测试
* @author wuyong
*
*/
public class SynchronizedTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建自定义线程类对象
MyThread mt = new MyThread();
//创建四个线程
Thread t = new Thread(mt);
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
Thread t3 = new Thread(mt);
//启动四个线程
t.start();
t1.start();
t2.start();
t3.start();
}
}
/**
* 自定义线程类,实现方式为,实现Runnable接口
* @author wuyong
*
*/
class MyThread implements Runnable{
//票数
private int tickets = 400;
Object obj = new Object();
/**
* 实现Runnable中的run方法,将线程执行的代码保存至run方法中
*/
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
//同步代码块
synchronized (obj) {//此处的对象参数 必须保证是同一个,否则同步失败
if (tickets > 0) {
//让线程休眠,此时如果不同步就会出现线程安全问题,
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//让票数递减
System.out.println(Thread.currentThread().getName()
+ ":ticket//" + tickets--);
} else break; //退出循环
}
}
}
}
多线程的综合示例:
生产者和消费者的模拟
package com.itheima.threaddemos;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 生产者和消费者的模拟
* 思路:
* 1,提供一个商品类,其中定义一个商品生产的状态标标识;
* 2,自定义一个生产者的线程类;
* 3,自定义一个消费者的线程类;
* 4,在自定义线程类的同步代码中,循环判断商品的生产状态。
* @author wuyong
*
*/
public class Pro_CusDemo {
/**
* @param args
*/
public static void main(String[] args) {
//创建一个商品对象
Goods goods = new Goods();
//创建生产线程
Pro_Thread pro = new Pro_Thread(goods);
//创建消费线程
Cus_Thread cus = new Cus_Thread(goods);
//创建4个线程
Thread tp1 = new Thread(pro);
Thread tp2 = new Thread(pro);
Thread tc1 = new Thread(cus);
Thread tc2 = new Thread(cus);
//开启线程,开始生产和消费
tp1.start();
tp2.start();
tc1.start();
tc2.start();
}
}
/**
* 商品类
* @author wuyong
*
*/
class Goods{
//商品名称
private String name;
//商品数量
private int counts;
//商品生产状态
private boolean flag;
//JDK1.5后的锁对象
Lock lock = new ReentrantLock();
//生产者的Condition对象
Condition pro_con = lock.newCondition();
//消费者的Condition对象
Condition cus_con = lock.newCondition();
/**
* 生产的方法
* JDK1.5之前的方式,用同步来实现
* @param name
*/
public synchronized void produce(String name){
while (flag) {//循环判断标识
try {
wait();
} catch (Exception e) {
e.printStackTrace();
}
}
counts++;
this.name = name + counts;
System.out.println(Thread.currentThread().getName() + "生产了++++++" + this.name);
flag = true;
notifyAll();
}
/**
* 消费的方法
* JDK1.5之前的方式,用同步来实现
* @param name
*/
public synchronized void customer(){
while (!flag) {//必须要循环判断标识!!!!
try {
wait();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "消费了" + name);
flag = false;
notifyAll();
}
/**
* 生产的方法
* JDK1.5 中提供的多线程升级解决方案。
* 将同步Synchronized替换成现实Lock操作。
* 将Object中的wait,notify notifyAll,替换了Condition对象。
* 该对象可以Lock锁 进行获取。
* 该示例中,实现了本方只唤醒对方操作。
* Lock:替代了Synchronized
* lock //获得锁
* unlock //释放锁,这一步必须要执行
* newCondition() //获得Condition对象
*
* Condition:替代了Object wait notify notifyAll
* await(); //线程等待
* signal(); //唤醒单个线程
* signalAll();//唤醒所有线程
*
* @param name
*/
public void produce2(String name){
try {
lock.lock();
while (flag) {
try {
pro_con.await();
} catch (Exception e) {
e.printStackTrace();
}
}
counts++;
this.name = name + counts;
System.out.println(Thread.currentThread().getName() + "生产了++++++" + this.name);
flag = true;
cus_con.signal();
} finally {
lock.unlock();
}
}
/**
* 消费的方法
* JDK1.5 中提供的多线程升级解决方案。
*/
public void customer2(){
try {
lock.lock();
while (!flag) {//必须要循环判断标识!!!!
try {
cus_con.await();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "消费了" + name);
flag = false;
pro_con.signal();
} finally{
lock.unlock();
}
}
}
/**
* 生产者
* @author wuyong
*
*/
class Pro_Thread implements Runnable{
private Goods goods;
public Pro_Thread(Goods goods){
this.goods = goods;
}
@Override
public void run() {
while (true) {
//JDK1.5之前的等待唤醒机制
goods.produce("金克拉");
//JDK1.5之后的等待唤醒机制
// goods.produce2("金克拉");
}
}
}
/**
* 消费者
* @author wuyong
*
*/
class Cus_Thread implements Runnable{
private Goods goods;
public Cus_Thread(Goods goods){
this.goods = goods;
}
@Override
public void run() {
while (true) {
//JDK1.5之前的等待唤醒机制
goods.customer();
//JDK1.5之后的等待唤醒机制
// goods.customer2();
}
}
}
---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
wait(),notify(),notifyAll()用来操作线程为什么定义在Object类中
1,这些方法存在于同步中。
2,使用这些方法时必须要标识所属的同步锁。
3,锁可以是任意对象,所有任意对象调用的方法一定是定义在Object中。
wait(),sleep()有什么区别?
wait():释放资源,释放锁。
sleep():释放资源,不释放锁。
wait();//使线程等待
notify();//唤醒线程,通常是唤醒线程池中的第一个被等待的线程。使用notify方法容易只唤醒本方线程,而导致所有线程都等待。
notifyAll();//唤醒线程池中的所有线程。
等待唤醒的示例代码:
package com.itheima.threaddemos.ThreadCommunication;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程间通信测试,及其wait,notify,notifyAll方法的测试类
* @author wuyong
*
*/
public class ThreadCommunicationTest2 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Resorce2 res2 = Resorce2.getInstance();
InputThread2 in = new InputThread2(res2);
OutputThread2 out = new OutputThread2(res2);
Thread t1 = new Thread(in);
Thread t2 = new Thread(in);
Thread t3 = new Thread(out);
Thread t4 = new Thread(out);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
/**
* 资源类
* @author wuyong
*
*/
class Resorce2{
private static final Resorce2 res2 = new Resorce2();
private Resorce2(){};
public static Resorce2 getInstance(){return res2;}
//姓名
private String name;
//国家
private String country;
//状态标示,用于控制线程是否等待或者唤醒
private boolean flag;
private Lock lock = new ReentrantLock();
private Condition con_pro = lock.newCondition();
private Condition con_cus = lock.newCondition();
/**
* 设置资源类的属性,并切换线程的等待或者唤醒,同步方法
* @param name
* @param country
*/
public synchronized void set(String name,String country) {
// if (flag) {
while (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.country = country;
System.out.println("现在出场的球星是---" + this.name);
flag = true;
// this.notify();
this.notifyAll();
}
public void set2(String name,String country) {
lock.lock();
try {
while (flag)
con_pro.await();
this.name = name;
this.country = country;
System.out.println("现在出场的球星是---" + this.name);
flag = true;
con_cus.signal();
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
/**
* 打印出资源的信息,同步方法
*/
public synchronized void out() {
while (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(this.name + "---" + this.country);
flag = false;
this.notifyAll();
}
public void out2() {
lock.lock();
try {
while (!flag)
con_cus.await();
System.out.println(this.name + "---" + this.country);
flag = false;
con_pro.signal();
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
}
/**
* 设置资源信息线程类
* @author wuyong
*
*/
class InputThread2 implements Runnable{
private Resorce2 res2;
public InputThread2(Resorce2 res2){
this.res2 = res2;
}
@Override
public void run() {
int i = 0;
while (true) {
if (i == 0)
res2.set2("kobe", "Amarican");
else
res2.set2("姚明", "中国");
i = (i + 1) % 2;
}
}
}
/**
* 输出资源信息线程类
* @author wuyong
*
*/
class OutputThread2 implements Runnable{
private Resorce2 res2;
public OutputThread2(Resorce2 res2){
this.res2 = res2;
}
@Override
public void run() {
while (true) {
res2.out2();
}
}
}
所以都要使用在同步中,因为只有同步才具有锁。
为什么这些操作线程的方法都要定义在Object类汇总呢?
因为这些方法在操作同步线程时,都必须要标识他们所操作线程持有的锁,
只有同一个锁上的被等待线程,可以被同一个锁notify唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。
而锁可以是任意对象,所以任意对象调用的方法定义在Object类中。
注:当有多个线程同时操作资源时,会导致程序出现错误。因为没有判断标记。
要用while循环判断标记,和使用notifyAll方法唤醒等待中的线程来判断标记。
等待唤醒的示例代码:
package com.itheima.threaddemos.ThreadCommunication;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程间通信测试,及其wait,notify,notifyAll方法的测试类
* @author wuyong
*
*/
public class ThreadCommunicationTest2 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Resorce2 res2 = Resorce2.getInstance();
InputThread2 in = new InputThread2(res2);
OutputThread2 out = new OutputThread2(res2);
Thread t1 = new Thread(in);
Thread t2 = new Thread(in);
Thread t3 = new Thread(out);
Thread t4 = new Thread(out);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
/**
* 资源类
* @author wuyong
*
*/
class Resorce2{
private static final Resorce2 res2 = new Resorce2();
private Resorce2(){};
public static Resorce2 getInstance(){return res2;}
//姓名
private String name;
//国家
private String country;
//状态标示,用于控制线程是否等待或者唤醒
private boolean flag;
private Lock lock = new ReentrantLock();
private Condition con_pro = lock.newCondition();
private Condition con_cus = lock.newCondition();
/**
* 设置资源类的属性,并切换线程的等待或者唤醒,同步方法
* @param name
* @param country
*/
public synchronized void set(String name,String country) {
// if (flag) {
while (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.country = country;
System.out.println("现在出场的球星是---" + this.name);
flag = true;
// this.notify();
this.notifyAll();
}
public void set2(String name,String country) {
lock.lock();
try {
while (flag)
con_pro.await();
this.name = name;
this.country = country;
System.out.println("现在出场的球星是---" + this.name);
flag = true;
con_cus.signal();
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
/**
* 打印出资源的信息,同步方法
*/
public synchronized void out() {
while (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(this.name + "---" + this.country);
flag = false;
this.notifyAll();
}
public void out2() {
lock.lock();
try {
while (!flag)
con_cus.await();
System.out.println(this.name + "---" + this.country);
flag = false;
con_pro.signal();
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
}
/**
* 设置资源信息线程类
* @author wuyong
*
*/
class InputThread2 implements Runnable{
private Resorce2 res2;
public InputThread2(Resorce2 res2){
this.res2 = res2;
}
@Override
public void run() {
int i = 0;
while (true) {
if (i == 0)
res2.set2("kobe", "Amarican");
else
res2.set2("姚明", "中国");
i = (i + 1) % 2;
}
}
}
/**
* 输出资源信息线程类
* @author wuyong
*
*/
class OutputThread2 implements Runnable{
private Resorce2 res2;
public OutputThread2(Resorce2 res2){
this.res2 = res2;
}
@Override
public void run() {
while (true) {
res2.out2();
}
}
}