黑马程序员--Java面向对象——多线程——下

------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流!

面向对象

线程间的通讯通信

线程间通信-等待唤醒机制

wait(),notify(),notifyAll(),都使用在同步中,因为要对持有监视器(锁)的线程操作。

思考:

wait(), notify() ,notifyAll(),用来操作线程的方法为什么定义在了Object类中?

因为这些方法在操作同步中,线程同步,都必须要表示它们所操作线程持有的锁,只有同一个锁上的被等待线程,可以被同一个锁notify唤醒,不可以对不同锁中的线程进行等待唤醒。

也就是说,等待和唤醒必须是同一个锁。所以要使用在同步中,因为只有同步才具有锁。

wait()   sleep() 有什么区别?

wait()   :释放资源,释放锁 。

sleep()   :释放资源,不释放锁。

/*
线程间通讯:其实就是多个线程在操作同一个资源,但是操作的动作不同(一个是wait 一个是notify)。

*/

/*

定义了一个资源类。

里面有俩个个同步函数input(String name,String user) output() 使用的锁是this.

*/

class Res{

private String name;//定义了一个名字,用于把传递进行的名字赋值给name变量

private String user;//定义了一个信息,用于把传递进来的内容赋值给user变量

private boolean flag;//为了线程的通讯,定义了一个标记,默认值是false

private int count = 100;//定义了一个共享数据,由于资源是唯一的,所以不用被static修饰

/*对函数进行同步,使每次传递进行的数据只能存放一个,当然如果不存放数据的话,那么将无法继续取走*/

private Res(){}//为了保证资源的唯一性,所以使用了单例设计模式

private static Res res =null;//懒汉式,对象的延迟加载,你懂的大笑

public static Res getInstanceof(){ 

/*加入多重判断提高效率*/

if(res==null)

/*使用同步技术,加了一个锁,保证只能有一个线程能进去*/

synchronized(Res.class){

/*再次判断,如果引用变量值为空,就在本类中创建本类对象*/

if(res==null)

res = new Res();

}

/*返回这个类的本类对象*/
return res;

}

public synchronized void input(String name,String user){/*接受2个参数,一个是name,一个是user*/

while(flag){

/*判断标记是否为true,如果为true就wait,因为里面还有数据,无法继续存放*/

try{wait();}catch(Exception e){}

}

/*判断下要循环的共享数据数是否为0,为0则不进行赋值运算,不为0则进行赋值运算*/

if(count>0){

//把传递进行的name赋值给成员变量的name

this.name = name;

//把传递进来的user赋值给成员变量的user

this.user = user;

//为了线程的通讯,把标志改为真。

flag = true;

//唤醒所有正在等待的线程

/*

这里如果写成notify可以吗?答案是不可以的,因为那样就可能会产生死锁。

为什么会产生死锁呢?

因为就跟你们在看我博客的人一样,你们很多人可以一起看我的博客,

但是我写博客的时候就苦逼了,我只能一个人慢慢写。

*/

notifyAll();

//打印当前线程的名称和公司和用户和共享数据还有多少个用户要存储

System.out.println(Thread.currentThread().getName()+"...公司:"+name+"...用户..."+user+"..."+"已存入数据库...还有"+count+"客户需要存储");

}

else{

/*

如果共享数据的值(也就是客户的数量)为0了,那么将虚拟机退出让程序停掉(工程被你干完了,那么就可以下班了)。

*/

System.out.println("数据库已经满了,如果想要扩容,请付钱");/*这是我们想要的效果大笑*/

System.exit(0);

}

}

/*对函数进行同步,跟input函数进行互斥,使每次取出数据的时候只能取走input存放的数据(当然数据只有一个)

当然如果数据不取空的话,那么将无法继续存放*/

public synchronized void output(){//不用接受参数,因为是拿数据嘛

while(!flag){

/*判断标记是否为true,如果为true就wait,因为里面没有数据,无法继续取出*/

try{wait();}catch(Exception e){}

}

/*打印取出数据的线程名称和公司名称和客户和剩下多少个客户*/

System.out.println(Thread.currentThread().getName()+"..获得的数据是...公司:" +name + "用户:"+ user+"里面还有"+count--+"个用户");

//为了线程的互斥,把标志改为假。

flag = false;

/*唤醒一个正在等待的线程。因为拿东西的时候可以一个人拿而存东西的不行,只能一个一个存。

看博客 写博客的例子。

*/

notify();

       }
}

/*要实现多线程嘛,所以定义一个线程类实现Runnable接口,把资源的数据存进去,达到多线程的效果*/
class Input implements Runnable{

/*把构造参数传递进行的资源对象赋值给成员变量,使资源对象作用于整个类*/

private Res r;

/*

构造的时候,为了存取操作不错乱,这时就要保证资源的唯一性,把资源对象构造时进行参数传递进来。

*/

Input(Res r){

/*把资源对象的引用赋值给成员变量。*/

this.r = r;

}

/*覆盖Runnable接口的run方法*/

public void run(){

/*定义一个变量,使每次传递进去的资源对象数据实现切换的效果*/

int x = 0;

while(true){

/*为了程序更严谨,加入了延迟,因为客户的电脑在网络上操作的时候,有使也会卡的嘛(简单的说就是延迟吧,)*/

try{Thread.sleep(20);}catch(Exception e){}

/*如果变量x=0就存入"CSDN社区","CSDN——新长城"*/

if(x==0)

/*调用资源对象存数据的方法,传递俩个参数,一个是公司名称,一个是客户*/

r.input("CSDN社区","CSDN——新长城");

/*否则就存入"黑马程序员","黑马程序员——Xcc"。这样就不放真实姓名了*/

else

r.input("黑马程序员","黑马程序员——Xcc");

/*对x的值进行操作,x永远只有俩个结果,一个是0,一个是1,当然这里你可以定义布尔型的值的,也是可以实现切换的效果的*/
x=(x+1)%2;

}

}

}

/*要实现多线程嘛,所以定义一个线程类实现Runnable接口,把存进去的资源数据取出来,达到多线程的效果*/
class Output implements Runnable{

/*线程之间的互斥,你懂的嘛!必须是同一个资源嘛!就像我写的博客 我写也是这个博客,你们看也是这个博客,当我更新博客的时候,如果你们关注我,当然我就会互斥,也就是发送消息给你们的嘛。说到底也就是一个看博客,一个写博客。都是这个博客嘛*/

/*把构造参数传递进行的资源对象赋值给成员变量,使资源对象作用于整个类*/

private Res r;

Output(Res r){

/*把资源对象的引用赋值给成员变量。*/

this.r = r;

}

/*覆盖Runnable接口的run方法*/

public void run(){

while(true){

/*这我就不说了,说了就是滴答了!跟存的注释是一样的 还是简单说一下把,就是我取数据的时候,我电脑可能网速有点慢,就卡了一下。不过这是每次都卡一下的哦。大笑你懂的偷笑*/

try{Thread.sleep(20);}catch(Exception e){}

/*调用资源对象取的方法,获取存入的数据*/

r.output();

}

}

}

/*对工程进行测试*/

public class InputOutputTest{

/*主函数*/

public static void main(String[] args){

/*由于这个类的构造函数被私有化,如果这个类应该有提供一个获取本类对象的静态方法getInstanceof()*/

Res r = Res.getInstanceof();

/*创建了一个线程,并把线程要运行的代码已经封装的Runnable子类的run方法中当成参数进行传递,调用start方法,开启线程掉用线程的run方法。由于采用了多线程技术,所以就多搞几个线程,玩玩(难得学了多线程嘛)*/

new Thread(new Input(r)).start();

new Thread(new Output(r)).start();

new Thread(new Input(r)).start();

new Thread(new Output(r)).start();

}

}

/*

一个类似与数据库存取的小程序我们已经搞定了,相信你直接有了自信了,继续看我写的博客哦,后面更精彩。

当然嘛!葫芦的前段是小的,后面那段才是大的。

*/

线程间通信-数据库存取JDK5.0升级版
JDK1.5中,提供了许多线程解决方案,将同步synchrozed 替换成 显示 Lock 操作
Object 中的 wait ,notify,notifyAll,替换成了condition 对象。
该对象可以Lock锁,进行获取。

数据库存取实例更新,本方置换型对方的操作。

代码如下:

class Res{

private String name;

private String user;

private boolean flag;

private int count = 100;

private Lock lock = new ReentrantLock();

private Condition condition_input =   lock.newCondition();

private Condition condition_output = lock.newCondition();

private Res(){}

private static Res res = null;

public static Res getInstanceof(){

if(res==null)

synchronized(Res.class){

if(res==null)

res = new Res();

}

return res;

}

public void input(String name,String user){

lock.lock();

try{

while(flag){

try{condition_input.await();}catch(Exception e){}

}

if(count>0){

this.name = name;

this.user = user;

System.out.println(Thread.currentThread().getName()+"...公司:"+name+"...用户..."+user+"..."+"已存入数据库...还有"+count+"客户需要存储");

flag =true;

condition_output.signalAll();

}

else{

System.out.println("数据库已经满了,如果想要扩容,请付钱");

System.exit(0);

}

}

finally{

lock.unlock();

}

}

public void output(){

lock.lock();

try{

while(!flag){

try{condition_output.await();}catch(Exception e){}

}

System.out.println(Thread.currentThread().getName()+"..获得的数据是...公司:" +name + "用户:"+ user+"里面还有"+count--+"个用户");

flag = false;

condition_input.signal();

}

finally{

lock.unlock();

}

}

}

class Input implements Runnable{

private Res r;

Input(Res r){

this.r = r;

}

public void run(){

int x=0;

while(true){

try{Thread.sleep(20);}catch(Exception e){}

if(x==0)

r.input("CSDN社区","CSDN——新长城");

else

r.input("黑马程序员","黑马程序员——Xcc");

x=(x+1)%2;

}

} }

class Output implements Runnable{

private Res r;

Output(Res r){

this.r = r;

}

public void run(){

while(true){

try{Thread.sleep(20);}catch(Exception e){}

r.output();

}

}

}

public class InputOutputTest{

public static void main(String[] args){

Res r = Res.getInstanceof();

new Thread(new Input(r)).start();

new Thread(new Output(r)).start();

new Thread(new Input(r)).start();

new Thread(new Output(r)).start();

}

}

思考一下:单例设计模式的synchroized替换吗?为什么?

答案:想到不用想,直接回答no,因为如果替换起来比较麻烦,Java不是还保留synchronized关键字吗?目的简化了我们的书写,应该就是因为这个原因。

当然我不知道我的答案是正确还是错误。尴尬这只不过是我的直观想法。


多线程(停止线程)

stop() 方法已经过时,如何停止线程?

只有一种,run()方法结束。开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run()方法结束,也就是线程结束。

特殊情况:当线程处于冻结状态(不是运行状态),就不会读取到标记,线程就不会结束。

当没有指定的方式让冻结的线程恢复到运行状态时,需要对冻结进行清除,强制让线程恢复到运行状态,这样就可以操作标记让线程结束,Thread类提供该方法interrupt();

 无返回值interrupt()
中断线程。

注:线程对象.interrupt(); 强制让冻结状态的线程恢复到运行状态,线程对象冻结的地方会抛出InterruptedException异常,处理该异常时(catch){改变标记},来停止线程。

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;  //当发生异常,说明调用了interrupt方法强制线程清楚冻结,希望线程停

changFlag();

}

System.out.println(Thread.currentThread().getName()+"……run");

}

}

public void changFlag(){

flag = false;

}

}

public class StopDemo{

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 = 0;

while(true){

if(num++ == 60){

t1.interrupt();//让冻结的线程恢复到运行状态

t2.interrupt();

break;

}

System.out.println(Thread.currentThread().getName()+"……"+num);

}

System.out.println("over");

}

}

多线程(守护线程)(后台线程)

线程对象.setDaemon(true);将该线程标记为守护线程或用户线程。 

当正在运行的线程都是守护线程时,Java虚拟机退出,程序结束。

注意:一个线程被标示为守护线程,并不是他不运行了,他跟其他线程一样运行。只是当前台线程执行完后,这些线程不会结束。

09-多线程(Join方法)

Join():当A线程执行到了B线程的.Join()方法时,A线程就会让出CPU执行权,等待直到B线程执行完,A才会执行。

Join可以用来临时加入线程执行。

注意:碰见了谁的Join()方法,就只等谁。等他结束就执行。

线程对象.start();

线程对象.join(); 

t1.start();

t1.join();  //等待t1执行完,才继续执行主线程和其他线程,其他线程在期间是冻结的

t2.start();

--------------------------------------------------------------------------------------------------------------

t1.start();

t2.start();

t1.join(); //当主线程执行到这里,他会冻结,但是t2不会冻结,他和t1交替执行,主线程等t1执行完了才执行,不管t2有没有执行完。

10-多线程(优先级&yield方法)

优先级:线程对象.setPriority(intnewPriority);设置线程优先权,1-10,默认5,越大,抢到CPU执行权机会越大;

Thread.MAX_PRIORITY,  MIN_PRIORITY ,NORM_PRIORITY)

static void yield():暂停当前正在执行的线程,并执行其他线程,Thread.yield();

/*今天就到这里了88,睡觉去了*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值