黑马程序员_多线程

------- android培训java培训、期待与您交流! ----------
1 进程:是一个正在执行的程序
每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制元,一个进程中至少有一个线程

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

扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程

2 创建线程的第一种方式:继承Thread类
步骤:
 1定义类继承Thread类
 2复写Thread类中的run方法  目的:将自定义代码存储在run方法中让线程运行
 3调用线程是start方法
   该方法两个作用:启动线程,调用run方法

/*
多线程例子1:用继承Thread类的方法创建一个卖票程序
有100张票,4个窗口同时在卖票
*/
Class Ticket extends Thread
{
	Private static int ticket=100;//因为票只有100张并且被多个对象调用,所以定义为静态

	Public void run()//继承Thread类复写run方法,里面存放多线程要运行的代码
	{
	  While(true)
	  {
		//这几行代码被多线程调用容易出现安全隐患,所以把它放进同步代码块中去
	   	Synchronized(Ticket.class) 
		{ 
	        	If(ticket>0)
	        	{
	          		Try{Thread.sleep(10);}catch(Exception e){}
	          		System.out.println(Thread.currentThread().getName()+”…”+ticket--);
			}
		}
	 }
	}
}

Class TicketDemo
{
	Public static void main(String[] args)
	{
		Ticket t1=new Ticket();//创建线程1对象
		t1.start();
		Ticket 2=new Tikcet();//创建线程2对象
		T2.start();
	}
}

发现运行结果每一次都不同
因为多个线程都获取cpu的执行权,cpu执行到谁,谁就执行
明确一点,在某一个时刻,只能有一个程序在运行(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的结果
我们可以形象把多线程的运行行为在互相抢夺cpu的执行权
这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多久,cpu说了算


3 为什么要覆盖run方法呢?
Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,也就是说Thread类中的run方法,用于存储线程要运行的代码

4 线程都有自己默认的名称
Thread-编号  编号从零开始
static Thread currentThread():   获取当前线程对象 Thread.currentThread();
getName();获取线程名称
设置线程名称:setName或者构造函数

5 创建线程的第二种方式:实现Runnable接口
步骤:
1)定义类实现Runnable接口
2)覆盖Runnable接口中的run方法   将线程要运行的代码存放在该run方法中
3)通过Thread类建立线程对象
4)将Runnable接口的子类对象作为实际参数传递给Thread的构造函数
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为,自定义的run方法所属的对象是Runnable接口的子类对象
所以要让线程去执行指定对象的run方法,就必须明确该run方法所属对象


调用Thread类的start方法开启线程并启用Runnable接口子类的run方法
/*
多线程例子2:用实现Runnable方法创建一个卖票程序
有100张票,两个窗口
*/
Class Ticket implements Runnable
{
	private int ticket=100;
	
	public void run()
	{
		while(true)
		{
		//这几行代码被多线程调用容易出现安全隐患,所以把它放进同步代码块中去
		This代码Ticket对象
			Synchronized(this)
			{
				If(ticket>0)
				{
				try{Thread.sleeep(10);}catch(Exception e){}	
				System.out.println(Thread.currentThread().getName()+”…”+ticket--);					}
			}
		}
	}
}


Class Threa2Demo
{
	Public static void main(String[] args)
	{
		Ticket t=new Ticket();


		Thread t1=new Thread(t);
		t1.start();
		Thread t2= new Thread(t);
		t2.start();


	}
}

实现方式和继承方式有什么区别呢?
继承Thread:线程代码存放在Thread子类run方法中
实现Runnable,线程代码存放在接口的子类的run方法中

实现方式的好处:避免了单继承的局限性
在定义线程时,建议使用实现方式

/*
继承方法创建多线程的局限性例子说明:
*/
class Pson
{
	pivate String name;
	private int age;
	Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
}


//这里学生继承了人,那么如果学生类里面有些代码存在安全隐患要使用多线程,那么就不能使用就不能使用基础Thread类的方法创建多线程了,因为java规定一个类不能有多个父类,所以这种情况下只能用实现Runnable类来创建多线程
class Student extends Person
{
	Student(String name,int age)
	{
		super(name,age);
	}
}

6 如何找出那些代码是需要同步的呢?
明确那些代码是多线程运行代码(run方法里面就是多线程运行代码)
明确共享数据(run方法里面的一般成员变量都是共享数据)
明确多线程运行代码中那些语句是操作共享数据的


当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行


就是同步代码块:
synchronized(对象)
{
需要被同步的代码
}

对象如同锁,持有锁的线程可以在同步中执行
没有持有锁的线程即使获取cpu执行权,也进不去,因为没有锁

同步的前提:
1.必须是有两个或者两个以上的线程
2.必须是多个线程使用同一个锁

注意:必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源

7 同步函数用的是那一个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this
所以同步函数使用的锁是this


/*
多线程例子3: 用实现Runnable方法创建一个卖票程序,Thread0执行同步代码块中的代码,Thread1执行同步函数中的代码,以此来验证同步函数持有的锁是this
有100张票,两个窗口
*/
Class Ticket implements Runnable
{
	private int ticket=100;
	boolean flag=true;
	public void run()
	{
	  if(flag)
	   {
		while(true)
		{
		//这几行代码被多线程调用容易出现安全隐患,所以把它放进同步代码块中去
		This代码Ticket对象
			Synchronized(this)
			{
				If(ticket>0)
				{
				try{Thread.sleeep(10);}catch(Exception e){}	
				System.out.println(Thread.currentThread().getName()+”…”+ticket--);					}
			}
		}
       	    }


	   else
	     while(true)
	       show();
	}


	public void show();
	{
		if(ticket>0)
		{
			try{Thread.sleep()}catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+ticket--);
		}
	}
}


Class Threa2Demo
{
	Public static void main(String[] args)
	{
	Ticket t=new Ticket();


	Thread t1=new Thread(t);
	Thread t2= new Thread(t);
	t1.start();


	try{Thread.sleep(10);}catch(Exception e){}
	t.flag=false;
	t2.start();


	}
}

这个例子可以看出,当我们在同步代码块中的锁不是this的时候,程序会有安全隐患,代表同步函数和同步代码块持有的不是同一个锁,当同步代码块中的锁是this的时候,我们发现程序没有安全隐患了,由此我们可以推出,同步函数中的锁是this

如果同步函数被静态修饰后,使用的是什么锁呢?
通过验证,发现不再是this,因为静态方法也不可以定义this
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象
类名.class  该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象  类名.class


8 死锁
同步中嵌套同步,各自持有锁,都不给对方这时候就造成死锁了
/*
死锁例子1: 用实现Runnable方法创建一个卖票程序,Thread0执行同步代码块中的代码,Thread1执行同步函数中的代码,以此来验证同步函数持有的锁是this

有100张票,两个窗口
*/
Class Ticket implements Runnable
{
	private int ticket=100;
	boolean flag=true;
	Object obj=new Object()
	public void run()
	{
	  if(flag)
	   {
		while(true)
		{
		//这几行代码被多线程调用容易出现安全隐患,所以把它放进同步代码块中去
		This代码Ticket对象
			Synchronized(obj)
			{
			//同步代码块要进去同步函数中执行,持有的锁不同,大家互补想让造成死锁
			show();
			}
		}
       	    }


	   else
	     while(true)
		show();
   	}


	public void show();
	{
	//同步函数中要进去同步代码块中执行,持有的锁不同,大家互补想让造成死锁
  		synchronized(obj)
   		{
			if(ticket>0)
			{
				try{Thread.sleep()}catch(Exception e){}
				System.out.println(Thread.currentThread().getName()+ticket--);
			}
    		}
	}
}


Class Threa2Demo
{
	Public static void main(String[] args)
	{
	Ticket t=new Ticket();


	Thread t1=new Thread(t);
	t1.start();
	try{Thread.sleep(10);}catch(Exception e){}
	t.flag=false;
	Thread t2= new Thread(t);
	t2.start();


	}
}


/*死锁例子2
开启两个线程,线程1进来后拿到locka锁打印if locka然后它想执行下面的代码又要拿到lockb的锁,如果线程2拿到lockb的锁不放,那么就形成了死锁
*/
class Test Runnable
{
	boolean flag;
	Test(boolean flag)
	{
		this.flag=flag;
	}	
	if(flag)
	{
		synchronized(Lock.locka)
		{
			System.out.println(“if locka”);
			snychronized(Lock.lockb)
			{
				System.out.println(“if lockb”);
			}
		}
	}


	else
	{
		synchronized(Lock.lockb)
		{
			System.out.println(“else lockb”);
			synchronized(Lock.locka)
			{
				System.out.println(“else locka”);
			}
		}
	}
}


class Lock
{
	static Lock locka=new Lock();
	static Lock lockb=new Lock();
}


class TestDemo
{
	public static void main(String[] args)
	{
		Test ts1=new Test(true);
		Test ts2=new Test(false);


		Thread t1=new Thread();
		t1.start();


		Thread t2=new Thread();
		t2.start();
	}
}

9  wait;  notify();  notifyAll();
我们这里要注意sleep和wait的区别:
sleep是Threa类的方法而wait是锁里面的方法,有了锁才能调用wait
调用sleep:线程睡一会儿,放弃了cpu执行权,但是没有放开锁,所以锁里的其它线程虽然抢到了cpu执行权,但是都不能进锁里来,知道这个线程睡完觉,出了同步函数
调用wati:让线程等待,线程放弃cpu执行权,放开锁,锁里的其他线程可以进去同步

注意:同步函数里只有有一个活动线程
都使用在同步中,因为要对持有监视器(锁)的线程操作
所以要使用在同步中,因为只能由同步才具有锁

为什么这些操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中的线程时,都必须要标识他们所操作线程的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒
也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中

/*
生产者和消费者的例子
详解wait;  notify();  notifyAll();的用法
*/


class Res
{
	private String name;
	private String sex;
	private boolean flag=false;


	public synchronized void setRes(String name,String sex)
	{
		//当flag为true时生产者就等待,否则就生产
		//这里等待线程醒来之后要判断flag的值,以免造成安全隐患
		while(flag)
		   try{this.wait();}catch(Exception e){}
		
		this.name=name;
		this.sex=sex;
		System.out.println(Thread.currentThread().getName()+"生产者"+name+"..."+sex);


		flag=true;
		//这里用notifyAll唤醒所有线程,避免唤醒的是本方线程,注意如果用notify,它唤醒的是进入线程池中的第一个线程,有可能唤醒的是本方线程,造成所有的线程都在等待


		this.notifyAll();
	}


	public synchronized void getRes()
	{


		//当flag为false时消费者就等待,否则就消费
		while(!flag)
		  try{this.wait();}catch(Exception e){}


		System.out.println(Thread.currentThread().getName()+"消费者"+name+"-----"+sex);
		flag=false;
		this.notifyAll();
	}
}


class Input implements Runnable
{


	private Res r;
	Input(Res r)
	{
		this.r=r;
	}


	public void run()
	{
		int x=0;
		while(true)
		{
			if(x==0)
			{
				r.setRes("mike","man");
			}


			r.setRes("丽丽","女");
			x=(x+1)%2;
		}
	}	
}


class Output implements Runnable
{
	private Res r;
	Output(Res r)
	{
		this.r=r;
	}


	public void run()
	{
		while(true)
		r.getRes();
	}
}


class InputOutputDemo
{
	public static void main(String[] args)
	{
		Res r=new Res();
		Input in=new Input(r);
		Output out=new Output(r);


		Thread t1=new Thread(in);
		Thread t2=new Thread(out);


		Thread t3=new Thread(in);
		Thread t4=new Thread(out);


		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}


10  join
当a线程执行到了b线程b线程的.join()方法时,a就会等待.等b线程执行完,a才会执行  join可以用来临时加入线程执行


11 那个线程开启你的,你就属于那个组的
优先级设置:t1.setPriority(Thread.MAX_PRIORITY);

释放执行权:Thread.yield();会有中断异常InterrupttedException
作用是尽量的平均cpu的使用权

t1.setDaemon(true);
将该线程标记为守护线程或者运行线程(后台线程),在启动线程前运行
前台线程:所写的线程都是前台线程
当所有前台线程结束后,后台线程自动结束


interrupt中断方法
该方法是结束线程的冻结状态,使线程回到运行状态中来
t1.interrupt();

/*
Interrupt例子
这是一个强制清除冻结状态,让线程重新回到运行状态
*/


class StopThread implements Runnable
{
	boolean flag=true;
	
	public  void  run()
	{
		while(flag)
		{
			try
			{
				this.wait();
			}


			catch(InterruptedException e)
			{
			System.out.println(Thread.currentThread().getName()+"清除了冻结状态");
			flag=false;
			}
			System.out.println(Thread.currentThread().getName()+"我来啦");
		}
		System.out.println(Thread.currentThread().getName()+"哈哈,我在外面");
	}
}


class StopDemo
{
	public static void main(String[] args)
	{
		StopThread st=new StopThread();		


		Thread t1=new Thread(st);
		Thread t2=new Thread(st);
		t1.start();
		t2.start();


		//如果这里加上t1.join()主线程碰到t1的join方法,释放了cpu执行权,然后t1和同
就抢夺cpu,最后等t1执行完主线程才会执行,如果t1执行过程中遇到了wait方法时,会产生InterruptedException异常,让t1恢复正常状态
//t1.join();


		//如果这里把t1设为后台线程,因为没有设置的线程都默认为前台线程,那么等t2和主线程都运行完,t1就会自动结束
		// t1.setDaemon(true);


		int x=1;
		while(true)
		{
			if(x++==60)
			{
				t1.interrupt();
				t2.interrupt();
				
				break;
			}
			System.out.println(Thread.currentThread().getName()+x);
		}


		System.out.println(Thread.currentThread().getName()+"哈哈我是主线程");
	}
}


12 多线程的新特性
/*
生产者和消费者的升级版
java新特性
Lock代替了synchronized函数和代码块,Condition对象代替了同步中的wait,notify,notifyAll方法Condition对象把wait,notify,notifyAll方法封装了

获取锁:Lock lock=new ReentrantLock();
获取跟锁有关的Condition对象: Condition condition=lock.newCondition();
注意:
1 Condition中用await,signal和signalAll代替了wait,notify,notifyAll方法
2 以前一个锁只有一个对应的wait,notify,notifyAll方法,现在一个锁里面可以有多个await,signal和signalAll方法,因为一个锁可以获取多个Condition对象,而Condition对象又有对应的方法


condition_in.await();等待的是生产者的线程
condition_out.signal();唤醒消费者的线程


condition_out.await();等待的是消费者的线程
condition_in.await();唤醒生产者的线程
*/


import java.util.concurrent.locks.*;
class Res3
{
	private String name;
	private String sex;
	private boolean flag=false;
	private Lock lock=new ReentrantLock();


	private Condition condition_in=lock.newCondition();
	private Condition condition_out=lock.newCondition(); 


	public void setRes(String name,String sex)throws InterruptedException
	{


		lock.lock();


		//这里写try{}finally{}是为了避免当发生异常的时候,保证可以线程释放锁
		try
		{
		while(flag)


		//让生产者wait
		   condition_in.await();
		this.name=name;
		this.sex=sex;
		System.out.println(Thread.currentThread().getName()+"生产者"+name+"..."+sex);


		flag=true;
		//这里用notifyAll唤醒所有线程,避免唤醒的是本方线程,注意如果用notify,它唤醒的是进入线程	 
	池中的第一个线程,有可能唤醒的是本方线程,造成所有的线程都在等待		


		//叫醒消费者
		condition_out.signal();
		}


		finally
		{
			lock.unlock();
		}
	}


	public void getRes()throws InterruptedException
	{


		lock.lock();
		try
		{
		//当flag为false时消费者就等待,否则就消费
		while(!flag)
		  condition_out.await();


		System.out.println(Thread.currentThread().getName()+"消费者"+name+"-----"+sex);
		flag=false;
		condition_in.signal();
		}


		finally
		{
			lock.unlock();
		}
	}
}


class Input3 implements Runnable
{


	private Res3 r;
	Input3(Res3 r)
	{
		this.r=r;
	}


	public void run()
	{
		int x=0;
		while(true)
		{
			if(x==0)
			{
				
				try{r.setRes("mike","man");}catch(Exception e){}
			}


			try{r.setRes("丽丽","女");}catch(Exception e){}
			x=(x+1)%2;
		}
	}	
}


class Output3 implements Runnable
{
	private Res3 r;
	Output3(Res3 r)
	{
		this.r=r;
	}


	public void run()
	{
		while(true)
		try{r.getRes();}catch(Exception e){}
	}
}


class InputOutputDemo3
{
	public static void main(String[] args)
	{
		Res3 r=new Res3();
		Input3 in=new Input3(r);
		Output3 out=new Output3(r);


		Thread t1=new Thread(in);
		Thread t2=new Thread(in);


		Thread t3=new Thread(out);
		Thread t4=new Thread(out);


		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值