『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对多线程安全问题提供了专业的解决方式>>>同步代码快

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;
	}
	
	
	
	
	
	
}


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

 

等待唤醒机制:

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()

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

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值