java基础—7.多线程

一、概念及理解

|--进程:正在进行中的程序(直译)。
|--线程:进程中一个负责程序执行的控制单元(执行路径)。
1、一个进程中可以有多个执行路径,称之为多线程。
2、一个进程中至少要有一个线程。
3、开启多个线程是为了同时运行多部分代码,每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务。
|--多线程相对单线程的特点
|--多线程的好处:解决了多部分代码同时运行的问题。
|--多线程的弊端:线程太多,会导致效率的降低。
	其实,多个应用程序同时执行都是CPU在做着快速的切换完成的。这个切换是随机的。CPU的切换是需要花费时间的,从而导致了效率的降低。
	JVM启动时启动了多条线程,至少有两个线程可以分析的出来:
	1.执行main函数的线程,该线程的任务代码都定义在main函数中。
	2.负责垃圾回收的线程。

二、线程创建方法

|--继承Thread类:直接继承Thread类,必需实现run()方法;
Thead_Demo t = new Thread_Demo();
t.start();
|--实现Runnable接口:因为java是单继承,使用接口增强了代码的扩展性,必需实现run()方法,一般用此方法:
Runnable_Demo rd = new Runnable_Demo();
Thread t   =  new Thread(rd);
r.start();

|--卖票举例

package threadTest;
/**
 *买票线程的思考:
 *1.一个资源,多个线程执行,票和买票窗口是不同类别,分别建立Ticket类、TicWindow类;
 *2.由于是多个线程,所以要考虑到安全问题,用同步机制;
 *3.一个资源,多个操作,属于资源共享情况,有两种方法可以解决,这里用静态方法;(还有一种可以传递统一对象)
 * 
 */
public class TicketTest {

	public static void main(String[] args) {
		for (int i = 1; i < 4; i++) {
			new Thread(new TicWindow(String.valueOf(i))).start();		
		}
	}
}
//定义票的资源,要用到的资源及方法定义成静态
class Ticket{
	private static  int tic = 100;
	
	public static int sellTic(){//卖票的方法
		return tic--;
	}
	public static int getTic(){
		return tic;
	}
	
	public static void setTic(int tic) {//设置多少张票
		Ticket.tic = tic;
	}	
}
//定义买票窗口类,
class TicWindow implements Runnable {
	String name;
	public void run(){
		while(true){
			//同步代码块保证线程安全
			synchronized(TicWindow.class){		
				//判断是否有票,有则买,没有就退出线程
				if(Ticket.getTic()>0)
					try {
						Thread.sleep(50);
						System.out.println(Thread.currentThread().getName()+"第"+name+"窗口"+"卖出第 "+Ticket.sellTic()+" 张票");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}	
				else
					break;
			}
		}
	}
	TicWindow(String name){
		this.name=name;
	}
	
}

三、线程状态及线程常用方法

|--线程状态
|--运行	start()
|--冻结	wait()和sleep(),放弃了CPU的执行权
|--等待	有cpu的执行权,但还没执行
|--停止	run()方法结束,通过建立“生命周期标记”来控制run循环结束

|--线程常用方法
|--interrupt()     	清除中断状态(wait()/sleep()/join())
|--join()              	 暂停其它线程,执行指定线程
|--yield()             	暂停当前正在执行的线程对象,并执行其他线程。
|--setPriority()   	更改线程的优先级(一般用0、5、10级)
|--setDaemon() 	指定为守护线程,当所有线程都为守护线程时,程序停止
|--currentThread() 返回对当前正在执行的线程对象的引用
|--toString()        	返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

四、线程的安全(同步机制)

|--同步的两种方法:
|--Lock、Condition
	JDK1.5升级后的新特性,Lock取代了 synchronized,Condition取代了wait/notify/notifyAll,让程序等待唤醒机制更灵活
|--synchronized
|--synchronized(obj){}  代码块 
|--好处:解决了线程的安全问题
|--缺点:判断锁会耗费资源,线程较多时,影响程序运行效率
|--前提:需要有两个以上线程才上锁
|--synchronized函数:锁对象默认是this对象;如果是静态函数,锁对象默认是 类名.class
|--同步注意的两个点
|--死锁:两个不同的锁相嵌套,相互关联,相互不放锁,使线程停止运行的情况,用于程序的同步
|--同锁:针对线程之间的通信,必需用相同的锁

五、等待唤醒机制

等待唤醒机制其实是通过一个标记,合理运用wait() 和notify()/notifyAll()方法控制线程,最终达到控制CPU执行顺序的目的。等待的线程都存在于线程池中。notify()唤醒第一个线程,notifyAll()唤醒全部。
|--方法一  synchronized
	wait()和notify()定义在上帝类Object中的原因是,任意对象都可以作当作锁的对象(监视器),根据面向对象的向上转型的思维,定义上帝类Object类是通用的……
	只有同一个锁上的被等待线程,可以被同一个锁上notify()唤醒
|--方法二   Lock、Condition
	Lock类要手动加锁和关锁,一个锁上可以设置多个监视器

六、多线程之间的通信

|--思路 : 针对共同资源进行操作,一般模式:
class Res{
	//to do something
}
class T1 implements Runnable{
	Res r;
	T1(Res r){
		this.r =r;
	}
	public void run{}
}
class T2 implements Runnable{
	Res r;
	T2(Res r){
	this.r =r;
	}
	public void run{}
}
//然后在main方法中对同一对象操作
public static void main(String[] args ){
	Res r = new Res();
	T1   t1 = new T1(r);
	T2   t2 = new T2(r);
} 
|--线程通信的安全
	不同线程之间用同一锁,否则出错。可以统一用资源对象。
package threadTest;
/**
 *多线程之间通信考虑的问题:
 *1.资源共享:两种方法,static静态处理,IO流装饰类模式;
 *2.线程安全:synchronized,Lock,这里运用synchronized; 
 *3.线程运行次序,即等待唤醒机制:运用wait()/notify();
 *注意:
 *1.wait()抛出InterruptedException,需要处理;
 *2.wait()/notify()是定义在“上帝类中”,同一锁调用,如 p.wait(); 
 *步骤: 
 *1.建立资源类,操作类1、操作类2、操作类3…………操作类中new资源对象,并让对象一致;
 *2.多线程考虑安全问题,用synchronized函数;
 *3.主函数中建立对象,开启线程;
 * 
 */
public class InputOutputDemo {

	public static void main(String[] args) {
		Person p = new Person();
		
		Input in = new Input(p);
		Output out = new Output(p);
		
		new Thread(in).start();
		new Thread(out).start();
		//以上四句,可以简写成以下
		//new Thread(new Input(p)).start();
		//new Thread(new Output(p)).start();
	}

}
//1.定义资源类Person
class Person{
	private String name ;
	private String sex ;
	
	private boolean flag = false;//用于控制等待与唤醒的标记
	
	public synchronized void set (String name ,String sex){
		if(this.flag){
			try {this.wait();}catch (InterruptedException e) {
				e.printStackTrace();}
		}
		
		this.name= name;
		this.sex = sex;
		
		this.flag = true;
		this.notify();
	}
	public synchronized void out(){
		if(!this.flag){
			try {this.wait();}catch (InterruptedException e) {
				e.printStackTrace();}
		}
		System.out.println(this.name+"…………"+this.sex);
		this.flag = false;
		this.notify();
	}
}
//2.定义输入类Input
class Input implements Runnable{
	//定义资源对象,类似于IO流的装饰类
	Person p;
	int x =0;

	public void run() {
		while (true) {
			if (x == 0)
				p.set("mike", "man");
			else 
				p.set("丽丽", "女女女女女");
			x = (x + 1) % 2;
		}
	}
	Input(Person p){
		this.p = p;
	}
}
//3.定义输出类Output类
class Output implements Runnable{
	Person p ;

	public void run() {
		while(true){
			p.out();
		}		
	}
	Output(Person p){
		this.p = p;
	}
}
|--等待唤醒机制
	两个方法(synchronezied、lock),考虑到效率问题一般用lock/Condition方法,lock/Conditions可以更加具体控制、更加针对控制同一类操作线程。
package threadTest;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *多线程间通信测试:多个生产者同时生产,多个消费都同时消费
 *思考:多线程通信目前有两种方法:
 *1.static方法,
 *2.类似于IO流的装饰模式;
 *ps.static在内存中存在周期过长,对内存不友好,一般用装饰模式,即建立资源统一操作
 *步骤:
 *1.建立资源类,操作类1、操作类2、操作类3…………操作类中new资源对象,并让对象一致;
 *2.多线程考虑安全问题,等待唤醒优先用Lock/Condition方法,不同操作类用不同锁;
 *3.主函数中建立对象,开启线程;
 * 
 */
public class ProducerConsumerDemo {

	public static void main(String[] args) {
		Resource r = new Resource();
		
		new Thread(new Producer(r)).start();
		new Thread(new Producer(r)).start();
		
		new Thread(new Consumer(r)).start();
		new Thread(new Consumer(r)).start();
	}
}
//1.创建资源类Resource
class Resource{
	private int count=0;
	private String name ;
	private boolean flag=false;
	
	private Lock lock = new ReentrantLock();//父类引用指向子类对象
	private Condition condition_pro = lock.newCondition();//生产监视
	private Condition condition_con = lock.newCondition();//消费监视
	//生产方式,用Lock/Condition方法保证线程同步及等待唤醒
	public void set(String name){
		lock.lock();//关锁
		try {
			while(flag)
				condition_pro.await();
			this.name = name;
			this.count ++;
			Thread.sleep(50);
			System.out.println(Thread.currentThread().getName()+"……生产………"+this.name+this.count);
			flag = true;
			condition_con.signalAll();
		} catch (Exception e) {
			e.printStackTrace();
		}
		finally{
			lock.unlock();//开锁,关闭资源的语句定义在finally中
		}		
	}
	//消费方式,用Lock/Condition方法保证线程同步及等待唤醒
	public  void out(){
		lock.lock();//关锁
		try {
			while(!flag)
				condition_con.await();//消费都等待
			Thread.sleep(50);
			System.out.println(Thread.currentThread().getName()+"……消费…………………"+this.name+this.count);
			flag = false;
			condition_pro.signalAll();//唤醒生产者的线程			
		} catch (Exception e) {
			e.printStackTrace();
		}
		finally{
			lock.unlock();//开锁,关闭资源的语句定义在finally中
		}
	}
}
//2.创建生产者Producer
class Producer implements Runnable{
	Resource r;
	public void run(){
		while(true){
			r.set("箱子");
		}
	}
	Producer(Resource r){
		this.r = r;
	}
}
//创建消费者Consumer
class Consumer implements Runnable{
	Resource r;
	public void run(){
		while(true){
			r.out();
		}
	}
	Consumer(Resource r){
		this.r = r;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值