多线程机制


1. 进程和线程的区别
    进程:正在进行的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
    线程:进程内部的一条执行路径或者一个控制单元。
    区别:一个进程至少有一个线程;进程在执行过程中拥有独立的内存单元,而多个线程共享内存
2. 多线程利弊
   利:解决了多个程序同时运行的问题,提高效率
   弊:会导致效率降低,因为线程的执行是靠cpu的来回切换
3. 多线程的实现方法
   根据多线程应用程序继承或实现对象的不同采用两种方式:一种是直接继承Java线程类Thread;另一种是定义并执行对象实现Runnable接口。
   A:继承Thread类
        定义一个类继承Thread类
        复写Thread类中的run()方法,将线程的任务代码封装到run()方法中
        直接创建Thread类中的子类对象,创建线程
        调用start()方法,开启线程
   B:实现 Runnable接口
        定义一个类 实现Runnable接口
        覆盖接口的run()方法,将线程的任务代码封装到run方法中
        创建Runnable接口的子类对象作为参数传递给Thread类的构造函数,创建Thread类对象
        调用start()方法,启动线程
   两种方法的区别:
         实现Runnable接口避免了单继承的局限性;
         继承Thread类线程代码存放在Thread子类的run方法中,实现Runnable接口线程代码存放在接口的子类的run方法中     
4. 线程状态:
    新建:new一个Thread对象或者其子类对象就是创建一个线程,当一个线程对象被创建,但是没有开启,这个时候,
          只是对象线程对象开辟了内存空间和初始化数据。            
    就绪:新建的对象调用start方法,就开启了线程,线程就到了就绪状态。
          在这个状态的线程对象,具有执行资格,没有执行权。
    运行:当线程对象获取到了CPU的资源。
          在这个状态的线程对象,既有执行资格,也有执行权。
    冻结:运行过程中的线程由于某些原因(比如wait,sleep),释放了执行资格和执行权。
              当然,他们可以回到运行状态。只不过,不是直接回到。  而是先回到就绪状态。   
    死亡:当线程对象调用的run方法结束,或者直接调用stop方法,就让线程对象死亡,在内存中变成了垃圾。
5. 多线程安全问题
    A   原因:当程序的多条语句在操作线程共享数据时,由于线程的随机性导致一个线程的多条语句,执行了一部分还没执行结束,
       另一个线程抢到cpu执行权参与进来执行,此时就导致共享数据发生错误。
    B  解决方法:对多条操作共享数据的语句进行同步,一个线程在执行过程中其他线程不可以参与进来
6.同步的前提及利弊
   前提:保证两个以上线程
               多个线程同时使用同一个锁,即多条语句操作线程共享数据
              保证同步中只有一个线程运行
   利:解决多线程的安全问题  
   弊:多线程都需要判断锁,比较消耗资源
   同步有两种表现形式:同步函数和同步代码块;同步函数使用this锁, 同步函数被静态修饰后,使用的锁是该对象的类的字节码文件,同步代码块可以使用任意对象的锁

     如下售票的例子:
class Ticket implements Runnable
{
    private int tick = 1000;
    Object ob = new Object();
	public void run() 
	{
		while(true)
		{
			synchronized(ob)
			{
				if(tick>0)
				{
					try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
					System.out.println(Thread.currentThread().getName()+"    "+tick--);
				}
			}
		}
	}	
}


class TicketDemo
{
	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();
	}
}
7. 单例设计模式
    饿汉式
class Single //饿汉式
{
	private Single(){} //构造方法私有,不让其他类建立该类对象
	private static final Single s = new Single();//自己建立一个对象
	public static Single getInstance()//提供一个公共访问方式
	{
		return s;
	}
}

懒汉式 
class Single //懒汉式
{
	private Single(){} 
	private static final Single s;
	public static Single getInstance()
	{
		if(s == null)
		{
			synchronized(Single.class)//避免线程安全问题
			{
				if(s == null)
					s = new Single();
			}
		}
		return s;
	}
}

8. 死锁:同步嵌套同步,并且锁不一样

9.多线程间通信:多个线程在操作同一个资源,但是操作动作不同。
     A:为什么要通信
        多线程并发执行的时候,如果需要制定线程等待或者唤醒指定线程,那么就需要通信。比如生产者消费者的问题,生产一个消费一个,生产的时候需要负责消费的进程等待,生产一个完成后需要唤醒负责消费的线程,同时让自己 处于等待,消费的时候负责消费的线程被唤醒,消费完生产的产品后又将等待的生产线程唤醒,然后处于等待。这样来回通信,以达到生产一个消费一个的目的。
     B:如何通信
        在同步代码块中,使用锁对象的wait()方法可以让当前线程等待,直到有其他线程唤醒为止。使用锁对象的notify()方法可以唤醒一个等待的线程,或者notifyAll唤醒所有等待的线程。多线程间通信用sleep很难实现,睡眠时间很难把握。

消费者生产者例子
class ProducerConsumeDemo 
{
	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();
	}
}

class Resource
{
	private String name;
	private  boolean flag = false;
	private int count = 1;
	
	public synchronized void set(String name)
	{
		while(flag)
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		this.name = name+"    "+count++;
		System.out.println(Thread.currentThread().getName()+"  消费者              "+this.name);
		flag = true;
		this.notifyAll();
	}
	
	public synchronized void out()
	{
		while(!flag)
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		System.out.println(Thread.currentThread().getName()+"  生产者              "+this.name);
		flag = false;
		this.notifyAll();
	}
}

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

class Consumer implements Runnablehttp://write.blog.csdn.net/postedit/8773950
{
	private Resource res;
	Consumer (Resource res)
	{
		this.res = res;
	}
	public void run() 
	{
		while(true)
		{
			res.out();
		}
	}
	
}

10. wait()、sleep()、notify()、notifyAll()
       wait()使一个线程处于等待状态,并且释放所持有的对象的lock;
       sleep()使一个正在运行的线程处于睡眠状态,是一个静态方法,捕捉InterruptedException异常
       notify()唤醒一个处于等待的线程,调用此方法时并不能确切的唤醒某一个等待状态的线程,而是由JVM确定,一般是最先开始等待的线程,不是按照优先级。
       notifyAll()唤醒所以处于等待状态的线程,并不是给所有唤醒线程一个对象锁而是让它们竞争。
  
    wait()、notify()、notifyAll()这些操作线程的方法定义在Object类中:只存在于同步中;必须指定所属锁 ,即被那个锁调用这些方法;而锁可以是任意对象,所以任意对象调用的方法就定义在Object中。

11. 其他方法
      停止线程:stop方法已经过时
      中断线程:void interrupt()中断状态将被清除,同时收到一个InterruptedException
      守护线程:setDaemon(boolean on)主线程结束,守护线程自动结束
      void  join()等待该线程终止:当线程A执行到B线程的join方法时,A会等到B执行结束,A才执行
      yield()方法:暂停当前正在执行的线程对象,并执行其他线程


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值