『黑马程序员』---java--多线程+线程通讯

时间:2023-02-20 10:36:19

多线程在实际开发中无处不在,处处都在,具有着高性能批量操作多处理等优点

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------


进程:是一个正在执行中的程序,每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元(一个进程中至少有一个线程)

线程:就是进程中的一个独立的控制单元,线程控制着进程的执行


那么,如何在自己定义的代码中,自定义一个线程?

第一种方式:继承Thread类

>1,定义类继承Thread    >2,复写run方法   >3,调用线程的start方法(启动线程并调用run方法)

为什么要覆盖run方法?Thread类用于描述线程.该类就定义了一个功能[run]用于存储线程要运行的代码.

第二种方式:实现Runnable接口

>1,定义类实现Runnable接口>2,覆盖Runnable接口中的run方法>3,通过Thread类建立线程对象  

>4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数,为了让线程去执行制定的run方法,明确该run方法的所属对象

 >5,调用Thread类中的start方法开启线程并调用Runnable接口子类的run方法

两种方式都能创建,实现方式和继承方式又有什么区别?

避免了单继承的局限性.区别:线程代码存放位置不同.继承存放在Thread子类的run方法中.而实现则是存放在Runnable接口的子类run方法中.

线程运行时的状态:

『黑马程序员』---java--多线程+线程通讯

多线程安全问题:

问题原因,当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,

还没执行完,别一个线程也参与进来执行,导致共享数据的错误.

解决办法:对多调操作共享数据的语句,控制,只能让一个线程都执行完毕后,再让其他线程参与.

java对多线程安全问题提供了专业的解决方式>>>同步代码快

Synchronized(对象){  //对象如同一把锁,持有锁的线程可以在同步中执行.

需要被同步的代码

}

同步的前提:1,必须要有两个或两个以上的线程

2,必须是多个线程使用同一个锁

好处:解决多线程的安全问题.弊端:多个线程需要判断锁,比较消耗资源


如何找多线程中存在的安全问题?

1,明确哪些代码是多线程运行的代码run()里的全是

2,明确共享数据[一般成员都是]

3,明确多线程运行代码中哪些语句是操作共享数据的


同步函数:

函数返回值前加上Synchronized,即可[他使用的锁是,this]

如果同步函数被static静态修饰后,使用的锁为,该类对应的字节码文件对象[类名.class]唯一性


Synchronized的应用延迟加载单例模式:

public class Test7 {
public static void main(String[] args){//建立Main函数,用来验证单例模式
System.out.println("第7题:编写一个延迟加载的单例设计模式?");
System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");


//建立s对象(其实,是将唯一对的引用赋给他s)
singleLhs s=singleLhs.getInstance();
System.out.println("s对象下调用的num值:"+s.Getnum());//先获取num
//在s下对num进行赋值操作
System.out.println("s对象下将num赋值为:“1314”");
s.Setnum(1314);
//建立ss对象(其实,还是将那个唯一对象的引用赋值给ss)
singleLhs ss=singleLhs.getInstance();
//在ss下获取并打印num,num的值依旧还是没有被初始化。还是被s下修改后的num。
System.out.println("ss对象下调用的num值:"+ss.Getnum());


System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
System.out.println("老师由次看来,我写的延迟加载单例设计模式成功了。");
System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}


}
//建立单例类
class singleLhs{
private int num=100;//定义一个被私有化的成员变量来,测试,单例模式。
public int Getnum(){//建立获取num的方法
return num;
}
public void Setnum(int num){//建立设置num的方法
this.num=num;//用到this关键字,在这主要为解决成员变量与局域变量重名的问题。this指的是当前对象的引用。
}
private static singleLhs sLhs=null;//
private singleLhs(){}//私有化构造函数,单例模式的核心,只要私有了它,就不能给对象初始化,就能控制随意新建对象了。
public static singleLhs getInstance(){//建立获取这个唯一对象的方法。
//使用双重if判断,可以解决懒汉式中同步的效率问题。
if(sLhs==null){//首先判断
synchronized(singleLhs.class){//同步时,使用的锁为,该类所属的字节码文件对象。
if(sLhs==null){//再次判断,设置关卡
sLhs=new singleLhs();//成功建立唯一的对象
}

}
}
return sLhs;
}






}

多线程间的通讯:其实就是多个线程在操作一个资源,但操作的动作不同

『黑马程序员』---java--多线程+线程通讯

等待唤醒机制:

wait(),notify(),notifyAll()

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

所以要使用在同步中,因为只有同步才具有锁

例子:

class Res
{
private String name;//定义名字
private String sex;//定义性别
private boolean flag=false;//定义标识,旗帜
public synchronized void set(String name,String sex){//定义方法接受数据
if(flag)//如果为真,代表有数据没输出,需要等待
try{this.wait();}catch(Exception e){}
this.name=name;
this.sex=sex;
flag=true;录入完数据,标识可以输出
this.notify();激活线程们
}
public synchronized void out(){//定义输出方法,将数据逐个输出
if(!flag)//判断是否有数据没有,则睡觉
try{this.wait();}catch(Exception e){}
System.out.println(name+"........."+sex);
flag=false;//取完数据,把旗帜表示为空,代表数据取走可以录入
this.notify();//激活线程
}
}
class Input implements Runnable//实现Runnable来自定义多线程
{
//资源对象
private Res r;
//构造函数,传进来一个对象,将引用赋给r
Input(Res r)
{
this.r=r;
}
public void run()//复写run,将多线程执行代码写入
{
int x=0;
while(true)
{
if(x==0)
r.set("mike","man");

else
r.set("丽丽","女");

x=(x+1)%2;
}

}

}
class Output implements Runnable//输出
{
private Res r;
Output(Res r){
this.r=r;
}
public void run(){
while(true){
r.out();
}

}

}
class Duoxc//测试
{
public static void main(String[] args){
Res r=new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}

这些操作方法都定义在Object中,又为什么?

因为这些方法在操作同步中线程时,都必须要标识他们所操作线程持有的锁,只有同一个锁上的被等待线程,才可以被同一个锁上的notify唤醒,不可以对

不同锁上的线程进行唤醒.锁可以是任意对象,所以可以被任意对象调用的方法一定要定义在Object类中.


Jdk1.5升级,里程碑,版本标识这从5.0走起,同时也给我们带来了许多神方法

在jdk1.5中,提供了多线程升级解决方案

lock,显示的锁机制

将同步Synchronized替换成显示lock操作.[显示的锁机制],其中将Object中的wait,notify,notifyAll,替换成了.condition对象.该对象通过lock锁获取.

在java.util,concurrent.locks中.一个锁可对应多个,支持多个相关的condition


lock实例:

import java.util.concurrent.locks.*;

class ProducerConsumerDemo//测试
{
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);//建立多线程
Thread t2=new Thread(pro);//建立多线程
Thread t3=new Thread(con);//建立多线程
Thread t4=new Thread(con);//建立多线程

t1.start();//调用start方法启动线程
t2.start();
t3.start();
t4.start();




}




}

class Resource//资源类
{
private String name;//定义名字
private int count=1;//计量器
private boolean flag=false;//旗帜,做标识
private Lock lock=new ReentrantLock();//建立lock
Condition condition_pro=lock.newCondition();//一个锁支持多个condition
Condition condition_con=lock.newCondition();//一个锁支持多个condition

public void set(String name){//数据录入
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();//唤醒对方输出线程
}catch(Exception e){}
finally{
lock.unlock();//最终关掉线程
}
}
public void out(){
lock.lock();//加锁
try{

while(!flag)//判断旗帜,有数据就输出,没有就等会
condition_con.await();
System.out.println(Thread.currentThread().getName()+"--销售---"+this.name);
flag=false;//输出完后,改下旗帜.标识已经清空
condition_pro.signal();唤醒对方,录入线程
}catch(Exception e){}
finally{
lock.unlock();//最终关掉锁
}

}


}
class Producer implements Runnable//实现Runnable复写run建立多线程执行代码
{
Resource res;
Producer(Resource res){
this.res=res;
}
public void run(){
while(true)//循环录入
res.set("商品");
}
}
class Consumer implements Runnable//实现Runnable复写run建立多线程执行代码
{
Resource res;
Consumer(Resource res){
this.res=res;
}
public void run(){
while(true)//循环取出
res.out();
}
}


停止线程[stop已经过时,该怎么办?]

1,定义循环结束标记

停止线程,只有一种,run方法结束.在线程运行代码中,大都是循环,只要能控制住循环即可.

2,使用interrupt(终端)方法

特殊情况下,当线程处于冻结状态,就不会取到标记.此时就需要用该方法结束线程的冻结状态,使线程回到运行状态.


新增的一些方法:

守护线程setDaemon(boolean)

该方法必须在启动线程前调用,将该线程标记为守护线程或用户线程,当正在运行的线程都为守护线程时,java虚拟机会退出


join()方法

当A线程执行到B线程的B.join() 方法时,A就会等待,等B线程都执行完成后,A线程才会执行,在他身后默默的守候.


String tostring()

返回该线程的字符串表示形式,包括线程名称,优先级,和线程组

  优先权setPriorty(int newPriority)更改线程的优先级,默认为5,一共分为(1~10)

MIN_PRIORITY最低  MORM_PRIORITY默认  MAX_PRIORITY最高


static void yield()

暂停当前正在执行的线程,并执行其他线程