JAVA之多线程

------- android培训java培训、期待与您交流! ----------

现在很多企业招聘JAVA方向的人才,都需要会多线程编程,可见其重要性。在很多应用中,多线程编程都占据很重要的部分,比如说,游戏,杀毒等。因此,我也用一篇技术博客专门介绍多线程编程。

先介绍进程:指正在进行中(执行)的程序,简单的说就是一个独立的应用程序。在windows里面,就是多进程的。

可以看见每一个.exe的程序都是一个进程。

线程:指进程中的一个独立的控制单元,线程在控制进程的执行。我个人理解,进程的运行,实际上是由多个线程同时运行的结果。即从微观上而言,应用程序的运行就是多个线程在同时执行的结果。宏观而言,就是一个进程在运行。线程是CPU调度资源的最小单位。

java VM 启动时候会有一个进程java.exe
该进程中至少一个线程负责java程序的执行
而且这个线程运行的代码存在于main方法中
该线程称之为主线程
扩展:其实更细节说明JVM,JVM启动不止一个线程,还有负责垃圾回收机制的线程,故属于多线程

关于进程我们理解了就行。重点介绍多线程

一个线程从产生到消亡,可能会经历的几种状态

理解这五种状态是我们能够熟悉掌握多线程编程的基础。

在平时应用中我们创建一个线程的方式有两种方式:

方式一:继承Thread类

创建线程的第一种方式:继承Thread类
 步骤:
 1,定义类继承Thread。
 2,复写Thread类中的run方法
 3,调用线程的start方法
 该方法的两个作用:启动线程,调用run方法
 多个线程都获取cpu的执行权,实际上cpu随机分给一个线程一块随机的时间片
 当线程用完时间片以后,就把cpu的使用权让出来给别的线程,总之,在某一时刻
 对应单核计算机,有且只有一个线程执行,由于cpu的快速切换,我们感觉不到切换
 的过程,我们可以形象的把多线程的运行行为在相互抢夺cpu的执行权

代码如下:

public class ThreadDemo extends Thread{

	@Override
	public void run() {//用于存储线程要执行的代码
	System.out.println("Hello world!");
	}

}
方式二:实现Runnable接口

步骤:

public class RunableDemo implements Runnable {

	private int ticket=100;
	Object ob=new Object();
	@Override
	public void run() {//用于存储线程要执行的代码
		// TODO Auto-generated method stub
		//执行的代码
		}	
	}
但是,在实际应用中,我们通常会遇到多线程安全问题。多线程安全问题:当多条语句在操作同一线程共享数据是,一个线程对多条语句只执行了一部分,还没有执行完, 此时另一个线程参与进来执行,导致共享数据的错误。

解决办法:
 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
 java对于多线程的安全提供了专业的解决方式
  就是同步代码块

  synchronized(对象){
代码块
 }
同步的前提:
 * 1、必须要有两个或者两个以上的线程运行
 * 2、必须是多个线程使用同一个锁
 * 好处:解决了多线程的安全问题
 * 弊端:多个线程需要判断锁,较为消耗资源
注意:

非静态同步函数的对象锁为this

public synchronized void show(){//同步函数锁
		ticket++;
		System.out.println(Thread.currentThread().getName()+"runtime..."+ticket--);
	}

静态同步函数所使用的锁是该方法所在类的字节码文件对象,即类名.class,静态方法里的同步锁都是使用的是类的字节码对象

public static synchronized void show(){//静态同步函数锁
		ticket++;
		System.out.println(Thread.currentThread().getName()+"runtime..."+ticket--);
	}

当我们创建一个对象时,通常会用到单例设计模式

饿汉式--不存在线程安全问题

public class Demo{
	private static final Demo demo=new Demo();
   private Demo(){
	}
   public static Demo getInstance(){
	   return demo;
}
}

单例设计模式--懒汉式:有延迟加载特性,导致存在多线程安全问题,所以,我们一般都会写为下面这种形式


public class Demo1{
	private static Demo1 demo=null;
	private Demo1(){
	}
	public static Demo1 getInstance(){
		if(demo==null){
		synchronized(Demo1.class)
		{
		if(demo==null)
			demo= new Demo1();
		}
		}
		 return demo;
	}
}

死锁:导致死锁的原因是,两个线程互相等待资源,导致两边都无法得到自己的资源,而使自己无法运行。

class Demo1{
	static Object obj1=new Object();
	static Object obj2=new Object();
}
class Demo2 implements Runnable{
	boolean flag;
	Demo2(boolean flag){
		this.flag=flag;
}
	@Override
	public void run(){
		if(flag){
			while(true){
				synchronized(Demo1.obj1){
					System.out.println("1");
					synchronized(Demo1.obj2){
						System.out.println("2");
					}
					}
				}
			}
		  else{
	    	  while(true){
					synchronized(Demo1.obj2){
						System.out.println("2");
						synchronized(Demo1.obj1){
							System.out.println("1");
						}
						}
					}
		}	
		}	
}

怎样将一些代码放入同步代码块里

步骤
 * 1、明确哪些代码是多线程运行代码
 * 2、明确共享数据
 * 3、明确多线程运行代码中哪些语句是操作共享数据的

下面代码可以具体说明

两个线程之间相互通信


public class ThreadConnectionDemo {
String name;
String sex;
boolean flag;
}

写入数据

/*
 * 线程间进行通信
 */
public class InputDemo implements Runnable{
 private ThreadConnectionDemo demo;
 InputDemo(ThreadConnectionDemo demo){
	 this.demo=demo;
 }
	@Override
	public void run() {
		// TODO Auto-generated method stub
		int i=0;
		while(true){
			synchronized(demo){
				try{Thread.sleep(500);}catch(Exception e){}
				if(demo.flag)
					try{demo.wait();//必须放在同步快里,设置线程到等待状态
					}catch(Exception e){}
				if(i==0){
					demo.name="zhangsan";
					demo.sex="man";
				}else{
					demo.name="李四";
					demo.sex="女";
				}
				i=(i+1)%2;
				demo.flag=true;
				demo.notify();//必须放在同步快里面,唤醒线程
			}
		}
	}

}

取出数据

public class OutputDemo implements Runnable {
private ThreadConnectionDemo demo;
OutputDemo(ThreadConnectionDemo demo){
	this.demo=demo;
}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			synchronized(demo){
				if(!demo.flag)
					try{demo.wait();}catch(Exception e){}
				System.out.println(demo.name+"......"+demo.sex);
				demo.flag=false;
				demo.notify();
			}
		}
	}

}

把同步代码放在同步函数里面

public class ResourceConDemo {
private String name;
private String sex;
private int count=0;
public boolean flag=false;
public synchronized void set(String name,String sex){
	if(flag)//避免多个线程安全问题
		try{this.wait();}catch(Exception e){}//wait()方法会抛出InterruptException异常
	this.name=name+"...."+count++;
	this.sex=sex;
	System.out.println(Thread.currentThread().getName()+"放入商品..."+this.name+"...."+sex);
	flag=true;
	this.notify();
}
public synchronized void out(){
	if(!flag)
		try{this.wait();}catch(Exception e){}
	System.out.println(Thread.currentThread().getName()+"取出商品..."+name+"............"+sex);
	this.notify();
	flag=false;
}
}

多个生产者和多个消费者问题,注意,保证共享数据安全

public class ResourceConDemo {
private String name;
private String sex;
private int count=0;
public boolean flag=false;
public synchronized void set(String name,String sex){
	while(flag)//避免多个线程安全问题,避免生产和消费不一致情况
		try{this.wait();}catch(Exception e){}
	this.name=name+"...."+count++;
	this.sex=sex;
	System.out.println(Thread.currentThread().getName()+"放入商品..."+this.name+"...."+sex);
	flag=true;
	this.notifyAll();//唤醒所有的线程
}
public synchronized void out(){
	while(!flag)
		try{this.wait();}catch(Exception e){}
	System.out.println(Thread.currentThread().getName()+"取出商品..."+name+"............"+sex);
	this.notifyAll();
	flag=false;
}
}


生产者

public class MoreResource implements Runnable{
	private ResourceConDemo resource;
	MoreResource(ResourceConDemo resource){
		this.resource=resource;
	}
		@Override
		public void run() {
			// TODO Auto-generated method stub
while(true){
		try{
				resource.set("商品", "苹果");
			}catch(Exception e){}
			}
		}
}

消费者

public class MoreConsumers implements Runnable {
private ResourceConDemo resource;
MoreConsumers(ResourceConDemo resource){
	this.resource=resource;
}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			try{
			resource.out();
		}catch(Exception e){}
		}
	}
}

主函数

public class RCDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args)throws Exception {
		// TODO Auto-generated method stub
		ResourceConDemo t=new ResourceConDemo();
		new Thread(new MoreResource(t)).start();//两个生产者
		new Thread(new MoreResource(t)).start();
		new Thread(new MoreConsumers(t)).start();//两个消费者
		new Thread(new MoreConsumers(t)).start();
	}

}

JDK1.5新特性,引入Lock锁

jdk1.5 新特性 Lock锁 他替换了synchronized wait  notify等关键字
import java.util.concurrent.locks.*;
public class ResourceConDemo2 {
	private String name;
	private String sex;
	private int count=0;
	public boolean flag=false;
	private Lock lock=new ReentrantLock();
	private Condition condition_re=lock.newCondition();//一个lock锁可以由多个条件
	private Condition condition_con=lock.newCondition();
	public  void set(String name,String sex)throws InterruptedException{
		try{
		lock.lock();
		while(flag)
			condition_re.await();
		this.name=name+"...."+count++;
		this.sex=sex;
		System.out.println(Thread.currentThread().getName()+"放入商品..."+this.name+"...."+sex);
		flag=true;
		condition_con.signal();//唤醒消费者的线程
		}
		finally{
			lock.unlock();//释放锁
		}
	}
	public void out()throws InterruptedException{
		try{
		lock.lock();
		while(!flag)
			condition_con.await();
		System.out.println(Thread.currentThread().getName()+"取出商品..."+name+"............"+sex);
		condition_re.signal();//唤醒生产者的锁
		flag=false;
		}finally{
			lock.unlock();//释放锁
		}
	}
	}

注意:

只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒
不可以对不同锁中的线程进行唤醒
也就是说,等待和唤醒必须是同一个锁

调用wait()方法,导致当前线程等待,并放入到线程池里等待被唤醒,唤醒之后继续执行以前未执行的代码

Interrupted()强制把冻结状态的线程恢复到运行状态来,但是会抛出InterruptException异常
join方法:当A线程执行到了B线程的.join()方法是,A就会等待,等待B线程都执行完,A才会执行
作用:可以用来加入线程的执行
此外线程默认优先级为5

必须要明白的一点:CPU执行的任意时刻有且只有一个线程才拥有CPU的使用权,也就是说,任意时刻只有一个线程在运行,之所有多线程同时执行,是因为CPU切换的速度太快,导致我们无法感觉到。

总结:多线程编程,必须理解和掌握它的五种基本状态并且多联系,多分析,练多了就掌握了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值