多线程


Thread 多线程-java.lang.Thread描述线程的类

特点:随机性

进程:一个正在执行中的程序。每个进程都有一个执行顺序

该顺序是一个执行路径,或控制单元

线程:进程中一个独立的控制单元

 

一、概述

1.线程控制进程执行

2.一个进程至少有一个线程(主线程)

3.JVM启动一个程序,不止有一个线程,还有一个回收垃圾机制的线程

4. 线程的运行状态 

 

 

 5.获取线程信息的方法:

线程对象,线程名称

getName()/setName()

currentThread()//返回当前线程对象

 

二、创建线程

创建线程的2个方法

1.继承Thread类

2.实现Runnable接口

(一)继承Thread类

继承Thread-覆盖run/创建实例/调用start

public void run()//run()用于存放需要通过多线程执行的代码

void start()//start()使该线程开始执行

 

class New extends Thread{

    public void run(){

    //运行代码

    }

}

 

New t=new New();

t.start();

 

(二)实现Runnable接口

实现Runnable接口实现接口/实例做参数/调用start

class New implements Runnable{

    public void run(){

    //运行代码

    }

}

new Thread(new New()).start();

 

(三)两种方法的区别

实际上就是Thread的构造函数的继承和重载(子类空参数ctor与Thread(<T extendsRunnable> t))。

1.一种是继承,一种是实现

2.一种将运行代码放在继承的run方法中,一种将运行代码放在实现的run方法中

3.一种是单继承,不具备扩展性,另一种可以继承多个接口和其他类

 

三、多线程安全问题

原因:多条语句操作同一个线程间共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程就参与进来执行,导致共享数据出错。

解决:只让单一线程进行操作,操作结束其他线程才可以操作。

同步synchronized

同步前提:

1.两个以上的线程

2.多个线程使用同一个锁

优点:解决多线程安全问题

缺点:须要判断锁,消耗资源

 

1.同步代码块

         synchronized(obj){//…}

2.同步函数

         synchronized void func(){//…}

注意只把须要同步的代码抽取出来

 

同步函数不需要设置锁,因为函数都被对象调用,函数都有一个所属对象引用就是this.即同步函数的锁就是this

 

静态同步函数-因为静态函数进内存时尚未有本类对象,所以锁肯定不是this.是本类的字节码class对象。

 

如何寻找多线程中的安全问题

a.明确哪些代码是多线程运行代码。

b.明确共享数据。

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

 

写出一个死锁程序

(标记为真进入第一个代码块中,先进obj1的锁,修改标记为假,这时候需要标记真的线程等待,需要标记假的线程可以进入,如果发生死锁则,持有obj1的锁无法进入obj2,持有obj2的锁无法进入obj1)

if(true==bFlag){
	synchronized(obj1){
		//bFlag=!bFlag;
		synchronized(obj2){}
	}
}else{
	synchronized(obj2){
		//bFlag=!bFlag;
		synchronized(obj1){}
	}
}


 

四、单例设计模式

懒汉式:

class Single{
	private static Single s=null;
	private Single(){}
	public static Single getInstance(){
		if(null==s){
			s=new Single();
		}
		return s;
	}
}

线程安全:添加同步代码块

public static Single getInstance(){
	if(null==s){
		synchronized(Single.class){
			if(null==s){
				s=new Single();
			}
		}
	}
	return s;
}


五、线程间通信问题

多个线程操作同一个资源,但是动作不同。

操作同一个资源时,注意使用同一个锁。

 

等待唤醒机制

wait(),notify(),notifyAll()

 

定义在Object类中的原因

1. wait(),notify(),notifyAll()是用于同步的方法,必须要标识其所操作线程的锁。只有同一个锁上的wait可以被同一个锁上的notify唤醒。

2.锁可以是任一对象。而任一对象的超类是Object,所以将这三种方法归在Object下。

 

wait使当前线程进入等待,会抛出InterruptedException

notify在其他线程中使用,对持有同一个锁进入wait的线程进行唤醒。

waitnotify必须是不同线程使用相同的锁

 

线程运行时内存会建立线程池存放等待线程,notify会唤醒其中的等待线程,通常按照顺序唤醒。

 

wait()与sleep()区别:

wait():释放cpu执行权,释放锁。

sleep():释放cpu执行权,不释放锁。

 

notifyAll:在生产消费模型中,需要唤醒对方线程时,若只用notify,容易出现只唤醒本方线程的情况。导致程序中的所以线程都等待。

 

六、生产消费模型

生产方-producer

消费方-consumer

资源-resource

p/c:

1实现Runnable接口。

2所有p/c在构造函数中都传入同一个r对象。

3在p中资源++,在c中资源—

4改进3,将资源+-的动作提取出来放入r类中。

5r类

a设置一个flag,false时可以生产,true时可以消费。

b设置一个name,表示资源内容

c设置一个cnt,表示资源数量记录

6r类中资源+的方法:

当flag为真时,说明当前生产出的产品还没有被消费,则进入等待

当flag为假时,说明之前生产的产品已经被消费掉,则进行新的生产,资源数量+1;生产结尾将flag设置为true,并唤醒所有等待线程。

7r类中资源-的方法:

当flag为假时,说明当前没有生产出产品,则进入等待

当flag为真时,说明当前有生产出产品,则进行新的消费,资源数量-1;消费结尾将flag设置为false,并唤醒所有等待线程。

class Resource{
	boolean bFlag=false;
	int cnt=0;
	String content;
	public synchronized void produce(String name) { 
		if(bFlag){
			try{wait();}catch(InterruptedException e){}
		}
		content=name+" : "+(++cnt);
		System.out.println(Thread.currentThread().getName()+":produced:"+content);
		bFlag = true;
		notifyAll();
	}

	public synchronized void consume() { 
		if(!bFlag){
			try{wait();}catch(InterruptedException e){}
		}
		try{Thread.sleep(100);}catch(InterruptedException e){}
		System.out.println(Thread.currentThread().getName()+":\tconsumed:"+content);
		bFlag = false;
		notifyAll();
	}
}

class Producer implements Runnable{
	private Resource r;
	public Producer(Resource r) {
		super();
		this.r = r;
	}
	public void run() { 
		int i=0;
		while(true){
			if(0==i){
				r.produce("cake");
				
			}else{
				r.produce("cookie");
			}
			i=(++i)%2;
		}		
	}
}

class Consumer implements Runnable{
	private Resource r;
	public Consumer(Resource r) {
		super();
		this.r = r;
	}
	public void run() { 
		while(true){
			r.consume();
		}
	}
	
}

class test2
{
	public static void main(String[] args)
	{
		Resource r=new Resource();
		Producer p=new Producer(r);
		Consumer c=new Consumer(r);
		
		new Thread(p).start();
		//new Thread(c).start();//p/c只能各开启一个线程
		new Thread(c).start();
		//new Thread(p).start();
	}
}

七、Lock

新方法-只唤醒对方线程不唤醒本方:

java.util.concurrent.locks

Condition

         |--Lock 

synchronized 方法或语句的使用提供了对于每个对象相关的隐式监视器锁的访问,但却强制所有锁获取和释放均要出现在一个块结构中:当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的词法范围内释放所有锁。

Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock替代了 synchronized 方法和语句的使用,Condition替代了 Object 监视器方法的使用。

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。

 

Condition接口中有方法await(),signal(),signalAll();替代wait,notify,notifyAll

 

通用格式

Lock myLock=new ReentrantLock();//新建锁

myLock.lock();//上锁

// Condition con = myLock.newCondition();  //新建一个等待 set,可以为同一个对象提供多个

try{

// con.await();

//…

}finally{

mylock.unlock;//一定解锁

}

await(),signal(),signalAll()则需要配合对应的锁使用,比如myLock.await();

 

现在Lock可以对同一对象提供多个wait-set,即可以在资源中针对生产和消费用同一个Lock对象设置两个不同的Condition,生产和消费线程使用其对应的Condition。这样就可以区分开,实现多个生产消费线程。

import java.util.concurrent.locks.*;
class Resource{
	boolean bFlag=false;
	int cnt=0;
	String content;
	Lock myLock=new ReentrantLock();
	Condition conCon=myLock.newCondition();
	Condition conPro=myLock.newCondition();
	public void produce(String name) {
		myLock.lock();
		try{
			while(bFlag){
				conPro.await();
			}
			content=name+" : "+(++cnt);
			System.out.println(Thread.currentThread().getName()+":produced:"+content);
			bFlag = true;
			
			conCon.signalAll();
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			myLock.unlock();
		}
	}

	public void consume() {
		myLock.lock();
		try{
			while(!bFlag){
				conCon.await();
			}
			try{Thread.sleep(100);}catch(InterruptedException e){}
			System.out.println(Thread.currentThread().getName()+":\tconsumed:"+content);
			bFlag = false;
			conPro.signalAll();
		}catch(InterruptedException e){
			e.printStackTrace();
		}finally{
			myLock.unlock();
		}	
	}
}

class Producer implements Runnable{
	private Resource r;
	public Producer(Resource r) {
		super();
		this.r = r;
	}
	public void run() { 
		int i=0;
		while(true){
			if(0==i){
				r.produce("cake");
				
			}else{
				r.produce("cookie");
			}
			i=(++i)%2;
		}		
	}
}

class Consumer implements Runnable{
	private Resource r;
	public Consumer(Resource r) {
		super();
		this.r = r;
	}
	public void run() { 
		while(true){
			r.consume();
		}
	}
	
}class test2
{
	public static void main(String[] args)
	{
		Resource r=new Resource();
		Producer p=new Producer(r);
		Consumer c=new Consumer(r);
		
		new Thread(p).start();
		new Thread(c).start();
		new Thread(c).start();
		new Thread(p).start();
		new Thread(p).start();
	}
}

八、线程停止方法

1.run中设置停止标记,自然结束

2.线程处于冻结状态时,无法读取到标记,线程无法结束。可以使用Interrupt

interrupt方法可以用来请求终止线程:

当对一个线程调用interrupt方法时,线程的中断状态将被置位,将冻结状态的线程强制清除,恢复运行状态。当对一个阻塞的线程调用interrupt,会产生中断异常。可以在中断异常的捕获中修改标记。从而使run可以结束。

a在Runnable子类中,添加一个更改标记的方法run中设置结束标记try-catch捕获Interrupted异常catch中调用更改标记的方法

b在主线程中显示声明新的线程

c启动线程

d让线程进入冻结状态

e经过一段时间的运行后,主线程中对已声明的线程对象使用interrupt()方法。

package test;
class TestStop implements Runnable{	
	private boolean bFlag=true;
	public synchronized void run() {
		System.out.println("start :"+Thread.currentThread().getName());	
		while(bFlag){
			try{
				wait();
			}catch(InterruptedException e){
				stopAll();
			}			
			try{Thread.sleep(100);}catch(InterruptedException e){}
			System.out.println("in while :"+Thread.currentThread().getName());			
		}
		System.out.println("out of while :"+Thread.currentThread().getName());			
	}	
	public void stopAll(){
		bFlag=false;
	}
}

class test2
{
	public static void main(String[] args)
	{
		TestStop ts=new TestStop();
		int i=0;
		Thread ts1=new Thread(ts);
		ts1.start();
		Thread ts2=new Thread(ts);
		ts2.start();
		Thread ts3=new Thread(ts);
		ts3.start();
		while(6!=i++){
			try{Thread.sleep(100);}catch(InterruptedException e){}
			System.out.println(Thread.currentThread().getName());	
		}
		ts1.interrupt();
		ts2.interrupt();
		ts3.interrupt();
		System.out.println("over");
		
	}
}

九、守护线程及其他

setDaemon(boolean on)

若想将某个线程标记为守护线程。当线程启动前必须使用一次此方法。

t1=new Thread(…);

t1.setDaemon(true);

t1.start();

当在运行的线程都是守护线程时,JVM自动退出。

 

join()

当A线程执行到了b线程的join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。

yield()

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

setPriority()

设置优先级

       MAX_PRIORITY 最高优先级10

       MIN_PRIORITY   最低优先级1

       NORM_PRIORITY 分配给线程的默认优先级


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值