黑马程序员-java学习之多线程技术

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

一.进程和线程的概念

进程:就是一个正在运行中的程序,而线程是指一个程序内部的一条执行线索,也可以说是一个程序的独立运行单元,实际上,操作的系统的多进程实现了多任务并发执行,程序的多线程实现了进程的并发执行。多任务、多进程、多线程的前提都是要求操作系统提供多任务、多进程、多线程的支持。

Java程序中,JVM负责线程的调度。线程调度是值按照特定的机制为多个线程分配CPU的使用权。调度的模式有两种:分时调度和抢占式调度。分时调度是所有线程轮流获得CPU使用权,并平均分配每个线程占用CPU的时间;抢占式调度是根据线程的优先级别来获取CPU的使用权。JVM的线程调度模式采用了抢占式模式。

 

二.多线程创建的两种主要方式

第一种:继承Thread类创建线程:

步骤:

1,定义类继承Thread

2,复写Thread类中的run方法。

      目的:将自定义代码存储在run方法。让线程运行。

 

3,调用线程的start方法。

示例:

class Demo extends Thread
{
	public void run()//用于存储需要运行的代码
	{
		for(int x=0; x<60; x++)
			System.out.println("demo run----"+x);//显示另外一条线程运行的效果
	}
}
class ThreadDemo 
{
	public static void main(String[] args) 
	{
		Demo d = new Demo();//创建好一个线程。
		d.start();//开启线程并执行该线程的run方法。
		for(int x=0; x<60; x++)
			System.out.println("Hello World!--"+x);//显示主线程运行的效果	
	}
}


 

第二种:实现runnable接口创建多线程

步骤:1.定义所需要的类实现Runnable接口,并覆盖run方法;2.在主函数建立子类对象,把这个子类对象当作参数传入thread的构造函数,并由此得到一个thread对象;3.调用start,启动线程

 

      两种创建方式比较,实现runnable接口的方式有以下好处:1.适合多个相同程序代码的线程去处理同一资源的情况。把虚拟CPU(线程)同程序的代码,数据有效分离,较好的体现了面向对象的设计思想;2.可以避免java的单继承造成的局限,因为在java中一个类只能继承一个父类,而可以实现多个接口;3.有利于程序的健壮性,代码能被多个线程共享,代码与数据是相对独立的

 

一.多线程的安全问题

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。

解决方法:同步代码块

synchronized(对象)

{

      需要被同步的代码

}

对象如同锁。持有锁的线程可以在同步中执行。

没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

示例代码:卖票案例:

class Ticket implements Runnable
{
	private  int tick = 1000;
	Object obj = new Object();//任意一个对象被当成锁
	public void run()
	{
		while(true)
		{
			synchronized(obj)
			{
				if(tick>0)
				{
					//try{Thread.sleep(10);}catch(Exception e){}
					System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
				}
			}
		}
	}
}
class  TicketDemo2
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

线程同步的原理:当线程执行到synchronized的时候,检查传入的实参对象,并得到该对象的锁旗标,如果得不到那么此线程会被加入到一个与该对象的锁旗标想关联的等待线程池中,一直等到该对象的锁旗标被归还,线程池中的等待线程就会得到该锁旗标,然后继续执行下去,当线程执行完成同步代码块时,就会自动释放它占有的同步对象的锁旗标,一个用于synchronized语句中的对象称为一个监视器,当一个线程获得了synchronizedobject)语句中的代码块的执行权,就意味着它锁定了监视器,在一段时间内,只能有一个线程可以锁定监视器。当同步代码块中遇到break语句或抛出异常时,线程也会释放该锁旗标。

同步代码块的锁是任意的对象,而同步函数用的锁是this,静态同步函数使用的锁是class对象,即类名.class

 

一.死锁

简单来讲,死锁就是同步中嵌套同步,而锁却不同

示例代码:

class Test implements Runnable
{
	private boolean flag;
	Test(boolean flag)
	{
		this.flag = flag;
	}

	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(MyLock.locka)
				{
					System.out.println(Thread.currentThread().getName()+"...if locka ");
					synchronized(MyLock.lockb)
					{
						System.out.println(Thread.currentThread().getName()+"..if lockb");					
					}
				}
			}
		}
		else
		{
			while(true)
			{
				synchronized(MyLock.lockb)
				{
					System.out.println(Thread.currentThread().getName()+"..else lockb");
					synchronized(MyLock.locka)
					{
						System.out.println(Thread.currentThread().getName()+".....else locka");
					}
				}
			}
		}
	}
}


class MyLock
{
	static Object locka = new Object();
	static Object lockb = new Object();
}

class  DeadLockTest
{
	public static void main(String[] args) 
	{
		Thread t1 = new Thread(new Test(true));
		Thread t2 = new Thread(new Test(false));
		t1.start();
		t2.start();
	}
}

一.线程间的通信

1Sleep()和wait()间的区别

区别一:

sleepThread类的方法,是线程用来控制自身流程的,比如有一个要报时的线程,每一秒中打印出一个时间,那么我就需要在print方法前面加上一个sleep让自己每隔一秒执行一次。就像个闹钟一样。

waitObject类的方法,用来线程间的通信,这个方法会使当前拥有该对象锁的进程等待知道其他线程调用notify方法时再醒来,不过你也可以给他指定一个时间,自动醒来。这个方法主要是用走不同线程之间的调度的。

区别二

关于锁的释放,调用sleep方法不会释放锁(自己的感觉是sleep方法本来就是和锁没有关系的,因为他是一个线程用于管理自己的方法,不涉及线程通信)

      2.wait(),notify(),notifyAll()几个方法与线程等待唤醒机制

      wait():告诉当前线程放弃锁并进入睡眠状态,一直到其他线程获取到同一锁并调用notify()为止。

      notify():唤醒同一对象锁中调用wait的第一个线程。

      notifyAll():唤醒同一对象锁中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行。

案例:生产者消费者

class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;
			//  t1    t2
	public synchronized void set(String name)
	{
		while(flag)//遇到生产者有多个线程,则需要用while反复判断
			try{this.wait();}catch(Exception e){}//t1(放弃资格)  t2(获取资格)
		this.name = name+"--"+count++;

		System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
		flag = true;
		this.notifyAll();
	}


	//  t3   t4  
	public synchronized void out()
	{
		while(!flag)
			try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格)
		System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
		flag = false;
	this.notifyAll();//用notify可能会只唤醒本方线程,因为notify是唤醒最先等待的线程
	}
}

class Producer implements Runnable
{
	private Resource res;

	Producer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			res.set("+商品+");
		}
	}
}

class Consumer implements Runnable
{
	private Resource res;

	Consumer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			res.out();
		}
	}
}
class ProducerConsumerDemo 
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();

		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);

		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(pro);
		Thread t3 = new Thread(con);
		Thread t4 = new Thread(con);

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

此示例代码是比较通用的方式,适用于存在生产者有多个,消费者也有多个的情况




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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值