黑马程序员——多线程

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

一、线程的概念

进程:当前正在执行的程序,代表一个应用程序在内存中的执行区域。
线程:是进程中的一个执行控制单元,执行路径。一个进程中如果只有一个执行路径,这个程序称为单线程。一个进程中有多个执行路径时,这个程序成为多线程。
多线程的好处:它的出现可以同时执行多条路径,让多部分代码同时执行,提高了效率。
class Demo extends Object
{
	public void finalize()
	{
		System.out.println("demo ok");
	}
}


class  ThreadDemo
{
	public static void main(String[] args) 
	{

		new Demo();
		new Demo();
		new Demo();
		System.gc();
		System.out.println("Hello World!");
	}
}


二、Thread 线程的的基类

1、 使该线程开始执行;Java 虚拟机调用该线程的 run 方法 void start()
2、 获取当前线程的名字 final String getName()
3、获取当前线程对象的引用 static Thread currentThread()
4、获取线程的标识符 long getId()
5、中断线程 void interrupt()
6、改变线程名称 setName(String name) 
7、更改线程的优先级 setPriority(int newPriority) 
8、让当前线程休眠 sleep(long millis) 
9、暂停当前正在执行的线程对象,并执行其他线程 static void yield()

三、线程状态图


四、线程的创建方式

1、继承Thread类
(1)子类覆盖父类中的run方法,将线程运行的代码存放在run中。
(2)建立子类对象的同时线程也被创建。
(3)通过调用start方法开启线程。
2、实现Runnable接口
(1)子类覆盖接口中的run方法。
(2)通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。
(3)Thread类对象调用start方法开启线程。
/*
创建线程的第一种方式:继承Thread类。

创建线程的第二种方式:实现Runnable接口。

1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
	为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。
	所以要在线程对象创建时就必须明确要运行的任务。

4,调用线程对象的start方法开启线程。


实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。
	按照面向对象的思想将任务的封装成对象。
2,避免了java单继承的局限性。

所以,创建线程的第二种方式较为常用。




*/


class Demo implements Runnable//extends Fu //准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行。
					//通过接口的形式完成。
{
	public void run()
	{
		show();
	}
	public void show()
	{
		for(int x=0; x<20; x++)
		{
			System.out.println(Thread.currentThread().getName()+"....."+x);
		}
	}
}


class  ThreadDemo
{
	public static void main(String[] args) 
	{	
		Demo d = new Demo();
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		t1.start();
		t2.start();


//		Demo d1 = new Demo();
//		Demo d2 = new Demo();
//		d1.start();
//		d2.start();
	}
}



/*
class Thread 
{
	private Runnable r;
	Thread()
	{
	
	}
	Thread(Runnable r)
	{
		this.r  = r;
	}

	public void run()
	{
		if(r!=null)
			r.run();
	}

	public void start()
	{
		run();
	}
}
class ThreadImpl implements Runnable
{
	public void run()
	{
		System.out.println("runnable run");
	}
}
ThreadImpl i = new ThreadImpl();
Thread t = new Thread(i);
t.start();




class SubThread extends Thread
{
	public void run()
	{
		System.out.println("hahah");
	}
}
*/


以上两种方式的区别
(1)实现方式好处:避免了单继承的局限性。在定义线程时,建立使用实现方式。
(2)两种方式区别:继承Thread:线程代码存放Thread子类run方法中。实现Runnable,线程代码存在接口的子类的run方法。
class Demo extends Thread
{
	private String name;
	Demo(String name)
	{
		super(name);
		//this.name = name;
	}
	public void run()
	{
		for(int x=0; x<10; x++)
		{
			//for(int y=-9999999; y<999999999; y++){}
			System.out.println(name+"....x="+x+".....name="+Thread.currentThread().getName());
		}
	}
}




class ThreadDemo2 
{
	public static void main(String[] args) 
	{

		/*
		创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行。
		
		而运行的指定代码就是这个执行路径的任务。

		jvm创建的主线程的任务都定义在了主函数中。

		而自定义的线程它的任务在哪儿呢?
		Thread类用于描述线程,线程是需要任务的。所以Thread类也对任务的描述。
		这个任务就通过Thread类中的run方法来体现。也就是说,run方法就是封装自定义线程运行任务的函数。
		
		run方法中定义就是线程要运行的任务代码。

		开启线程是为了运行指定代码,所以只有继承Thread类,并复写run方法。
		将运行的代码定义在run方法中即可。 


		*/
//
//		Thread t1 = new Thread();

		Demo d1 = new Demo("旺财");
		Demo d2 = new Demo("xiaoqiang");
		d1.start();//开启线程,调用run方法。
		
		d2.start();
		System.out.println("over...."+Thread.currentThread().getName());
	}
}


五、线程安全问题

1,当线程中多条代码在同时操作共享数据。
2,是否被多条语句操作。
这也是判断多线程程序是否存在安全隐患的依据。
3,解决安全问题的方式:java中提供了一个同步机制。
4,解决原理:让多条操作共享数据的代码在某一时间段,被一个线程执行完,在执行过程中,其他线程不可以参与运算。

六、同步(synchronized)代码块

1、同步的特点:
(1)同步的前提,同步需要两个或者两个以上的线程。多个线程使用的是同一个锁。
(2)同步的好处,同步的出现解决了多线程的安全问题。
(3)同步的弊端,当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
2、同步锁的分类:
(1)Class字节码,静态的同步函数
(2)this,同步函数(就是在函数上加上synchronized修饰符,使用的this锁)
/*
同步函数的使用的锁是this;

同步函数和同步代码块的区别:
同步函数的锁是固定的this。

同步代码块的锁是任意的对象。

建议使用同步代码块。


*/
class Ticket implements Runnable
{
	private  int num = 100;
//	Object obj = new Object();
	boolean flag = true;
	public void run()
	{
//		System.out.println("this:"+this);

		if(flag)
			while(true)
			{
				synchronized(this)
				{
					if(num>0)
					{
						try{Thread.sleep(10);}catch (InterruptedException e){}						
						System.out.println(Thread.currentThread().getName()+".....obj...."+num--);
					}
				}
			}
		else
			while(true)
				this.show();
	}

	public synchronized void show()
	{
		if(num>0)
		{
			try{Thread.sleep(10);}catch (InterruptedException e){}
			
			System.out.println(Thread.currentThread().getName()+".....function...."+num--);
		}
	}
}

class SynFunctionLockDemo 
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();
//		System.out.println("t:"+t);

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

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


(3)已创建好的对象
/*
静态的同步函数使用的锁是  该函数所属字节码文件对象 
可以用 getClass方法获取,也可以用当前  类名.class 表示。


*/

class Ticket implements Runnable
{
	private static  int num = 100;
//	Object obj = new Object();
	boolean flag = true;
	public void run()
	{
//		System.out.println("this:"+this.getClass());

		if(flag)
			while(true)
			{
				synchronized(Ticket.class)//(this.getClass())
				{
					if(num>0)
					{
						try{Thread.sleep(10);}catch (InterruptedException e){}						
						System.out.println(Thread.currentThread().getName()+".....obj...."+num--);
					}
				}
			}
		else
			while(true)
				this.show();
	}

	public static synchronized void show()
	{
		if(num>0)
		{
			try{Thread.sleep(10);}catch (InterruptedException e){}
			
			System.out.println(Thread.currentThread().getName()+".....function...."+num--);
		}
	}
}

class StaticSynFunctionLockDemo 
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();

//		Class clazz = t.getClass();
//		
//		Class clazz = Ticket.class;
//		System.out.println("t:"+t.getClass());

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

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


3、死锁(同步嵌套)
(1)原因:同步嵌套,多把锁的出现
(2)解决办法,使用唯一锁
class Ticket implements Runnable
{
	private  int num = 100;
	Object obj = new Object();
	boolean flag = true;
	public void run()
	{


		if(flag)
			while(true)
			{
				synchronized(obj)
				{
					show();
				}
			}
		else
			while(true)
				this.show();
	}

	public synchronized void show()
	{

		synchronized(obj)
		{
			if(num>0)
			{
				try{Thread.sleep(10);}catch (InterruptedException e){}
				
				System.out.println(Thread.currentThread().getName()+".....sale...."+num--);
			}
		}
	}
}


class DeadLockDemo 
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();
//		System.out.println("t:"+t);

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

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


七、线程间的通信

1、sleep() 睡眠 指定时间
2、wait()等待 可指定时间也可不指定
3、notify()/notyfyAll()唤醒线程
<pre name="code" class="java">/*
jdk1.5以后将同步和锁封装成了对象。 
并将操作锁的隐式方式定义到了该对象中,
将隐式动作变成了显示动作。

Lock接口: 出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成现实锁操作。
同时更为灵活。可以一个锁上加上多组监视器。
lock():获取锁。
unlock():释放锁,通常需要定义finally代码块中。


Condition接口:出现替代了Object中的wait notify notifyAll方法。
			将这些监视器方法单独进行了封装,变成Condition监视器对象。
			可以任意锁进行组合。
await();
signal();
signalAll();



*/

import java.util.concurrent.locks.*;

class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;

//	创建一个锁对象。
	Lock lock = new ReentrantLock();

	//通过已有的锁获取该锁上的监视器对象。
//	Condition con = lock.newCondition();

	//通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。
	Condition producer_con = lock.newCondition();
	Condition consumer_con = lock.newCondition();

	
	public  void set(String name)//  t0 t1
	{
		lock.lock();
		try
		{
			while(flag)
//			try{lock.wait();}catch(InterruptedException e){}//   t1    t0
			try{producer_con.await();}catch(InterruptedException e){}//   t1    t0
		
			this.name = name + count;//烤鸭1  烤鸭2  烤鸭3
			count++;//2 3 4
			System.out.println(Thread.currentThread().getName()+"...生产者5.0..."+this.name);//生产烤鸭1 生产烤鸭2 生产烤鸭3
			flag = true;
//			notifyAll();
//			con.signalAll();
			consumer_con.signal();
		}
		finally
		{
			lock.unlock();
		}
		
	}

	public  void out()// t2 t3
	{
		lock.lock();
		try
		{
			while(!flag)
//			try{this.wait();}catch(InterruptedException e){}	//t2  t3
			try{cousumer_con.await();}catch(InterruptedException e){}	//t2  t3
			System.out.println(Thread.currentThread().getName()+"...消费者.5.0......."+this.name);//消费烤鸭1
			flag = false;
//			notifyAll();
//			con.signalAll();
			producer_con.signal();
		}
		finally
		{
			lock.unlock();
		}
		
	}
}

class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.set("烤鸭");
		}
	}
}

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



class  ProducerConsumerDemo2
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);

		Thread t0 = new Thread(pro);
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(con);
		Thread t3 = new Thread(con);
		t0.start();
		t1.start();
		t2.start();
		t3.start();

	}
}


 
 


wait和sleep的区别:
对时间的指定。1,sleep方法必须指定时间。2,wait方法有重载形式,可以指定时间,也可以不指定时间。对于执行权和锁的操作
对于执行权和锁的操作:1,sleep():释放执行权,不释放锁,因为肯定能醒,肯定可以恢复到临时阻塞状态。2,wait():释放执行权,释放锁,因为wait不释放锁,如果没有时间指定,那么其他线程都进行不了同步中,无法将其唤醒。
wait 和 sleep 区别?

1,wait可以指定时间也可以不指定。
   sleep必须指定时间。

2,在同步中时,对cpu的执行权和锁的处理不同。
	wait:释放执行权,释放锁。
	sleep:释放执行权,不释放锁。



class Demo
{

	void show()
	{
		synchronized(this)// 
		{
		 
			wait();//t0 t1 t2
		
		}
	}
	void method()
	{
		synchronized(this)//t4
		{
		
			//wait();

			notifyAll();
		
		
		}//t4
	}
}




class  
{
	public static void main(String[] args) 
	{
		System.out.println("Hello World!");
	}
}



八、停止线程

1、定义循环结束标记,因为线程运行代码一般都是循环,只要控制了循环即可。
2、使用interrupt(中断)方法。该方法是结束线程的冻结状态,使线程回到运行状态中来。

九、Lock锁

1、获取锁:lock();
2、释放锁:unlock();注意:释放的动作一定要执行,所以通常定义在finally中。
3、获取Condition对象:newCondition();
4、将Object中的wait,notify,notifyAll方法都替换成了Condition的await,signal,signalAll。
5、和以前不同是:一个同步代码块具备一个锁,该所以具备自己的独立wait和notify方法。
6、现在是将wait,notify等方法,封装进一个特有的对象Condition,而一个Lock锁上可以有多个Condition对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值