多线程


      Java编写程序都运行在在Java虚拟机(JVM)中,在JVM的内部,程序的多任务是通过线程来实现的。每用java命令启动一个java应用程序,就会启动一个JVM进程。在同一个JVM进程中,有且只有一个进程,就是它自己。在这个JVM环境中,所有程序代码的运行都是以线程来运行。

多线程的目的是为了最大限度的利用CPU资源。
       一般常见的Java应用程序都是单线程的。比如,用java命令运行一个最简单的HelloWorld的Java应用程序时,就启动了一个JVM进程,JVM找到程序程序的入口点main(),然后运行main()方法,这样就产生了一个线程,这个线程称之为主线程。当main方法结束后,主线程运行完成。JVM进程也随即退出 。
       对于一个进程中的多个线程来说,多个线程共享进程的内存块,当有新的线程产生的时候,操作系统不分配新的内存,而是让新线程共享原有的进程块的内存。因此,线程间的通信很容易,速度也很快。不同的进程因为处于不同的内存块,因此进程之间的通信相对困难。实际上,操作的系统的多进程实现了多任务并发执行,程序的多线程实现了进程的并发执行。多任务、多进程、多线程的前提都是要求操作系统提供多任务、多进程、多线程的支持。
       所谓的“并发执行”、“同时”其实都不是指同一时刻同时运行,而是指在同一时间段内同时运行。操作系统将进程线程进行管理,轮流分配每个进程很短的一段是时间,然后在每个线程内部,程序代码自己处理该进程内部线程的时间分配,多个线程之间相互的切换去执行,这个切换时间也是非常短的。因此多任务、多进程、多线程都是操作系统给人的一种宏观感受,从微观角度看,程序的运行是异步执行的。

       一、Java通过继承Thread类实现多线程

<span style="font-size:14px;">package com.amorvos.ThreadTest;

class Ticket extends Thread {
	private static int num = 100;

	@Override
	public void run() {
		while (true) {
			if (num > 0) {
				System.out.println(Thread.currentThread().getName()
						+ "Sell Ticket  : " + num--);
			}
		}
	}

}

public class ThreadDemo {

	public static void main(String[] args) {

		Ticket t1 = new Ticket();
		Ticket t2 = new Ticket();
		Ticket t3 = new Ticket();
		Ticket t4 = new Ticket();

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

	}
}</span>
       程序启动运行该类的main方法时,java虚拟机启动一个进程。随着调用实例的start方法,启动了其他线程,整个应用就在多线程下运行。需要注意的是start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态,什么时候运行是由操作系统决定的。因此从程序运行的结果可以发现,多线程程序是乱序执行,只有乱序执行的代码才有必要设计为多线程(实际上所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的)。
       二、Java通过实现Runnable接口实现多线程

<span style="font-size:14px;">class Ticket implements Runnable {
	private static int num = 100;

	@Override
	public void run() {
		while (true) {
			if (num > 0) {
				System.out.println(Thread.currentThread().getName()
						+ "Sell Ticket  : " + num--);
			}
		}
	}

}

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

		Runnable runnable = new Ticket();
		Thread t1 = new Thread(runnable);
		Thread t2 = new Thread(runnable);
		Thread t3 = new Thread(runnable);
		Thread t4 = new Thread(runnable);

		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}</span>

       通过实现Runnable接口,使得该类有了多线程类的特征。Run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。实际上在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。这是代理模式的体现。
       三、线程的安全问题
       线程的运行状态如下图

       进程间的安全问题是由进程间的切换造成的。由于多线程是并发执行的,当多个线程操作某一数据块时操作可能是不可中断的,此时线程却进行了切换,造成数据的错误。这种不安全的现象并不是说一定会出现,而是说存在安全隐患。
       1、类内非静态方法采用同步代码块
       同步代码块需要指定任意对象作为同步的标志,必须保证多线程同步时使用的是同一把锁。
       改写上部线程不安全的代码:

@Override
	public void run() {
		while (true) {
			synchronized (object) {
				if (num > 0) {
					System.out.println(Thread.currentThread().getName()
							+ "Sell Ticket  : " + num--);
				}
			}
		}
	}

}

       2、类内的方法采用同步函数

class Ticket implements Runnable {
	private static int num = 100;
	
	public synchronized void show()
	{
		if (num > 0) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()
					+ "Sell Ticket  : " + num--);
		}
		
	}
	
	@Override
	public void run() {
		while (true) {
			  this.show();
		}
	}
}

       当采用同步非静态函数时,采用的锁是this;当采用同步静态函数时,采用的锁是类的二进制文件,也就是class文件。
       补充:单例类中的线程同步
       当单例类采用懒汉式时会存在线程安全问题。解决方式如下:

class Single {
	private static Single sg = null;

	private Single() {
	}

	public static Single getInstance() {
		if (sg == null) {
			synchronized (Single.class) {
				if (sg == null) {
					sg = new Single();
				}
			}
		}
		return sg;
	}
}
       四、、生产者消费者问题—Java中的等待唤醒机制
       等待唤醒机制实际上是应用了线程的阻塞状态。阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
       等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中,需要其他线程唤醒该线程才能继续进行。
       同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
       其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
<span style="font-size:14px;">package com.amorvos.ThreadTest;

class Resource {
	private String name;
	private int num;
	private boolean flag;

	public synchronized void set(String name) {
		while (flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name;
		num++;
		System.out.println(Thread.currentThread().getName()
				+ "..............生产者.............." + this.name);
		flag = true;
		notifyAll();
	}

	public synchronized void get() {
		while (!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName() + "..消费者.."
				+ this.name);
		flag = false;
		notifyAll();
	}
}

class Producer implements Runnable {
	private Resource r;

	public Producer(Resource r) {
		this.r = r;
	}

	@Override
	public void run() {
		while (true) {
			r.set("馒头");
		}
	}

}

class Customer implements Runnable {
	private Resource r;

	public Customer(Resource r) {
		this.r = r;
	}

	@Override
	public void run() {
		while (true) {
			r.get();
		}
	}

}

public class CustomerProductProblem {

	public static void main(String[] args) {

		Resource r = new Resource();
		Producer pro = new Producer(r);
		Customer cus = new Customer(r);
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(pro);
		Thread t3 = new Thread(cus);
		Thread t4 = new Thread(cus);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}
          五、Lock,Condition的使用
          Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。

package com.amorvos.ThreadLockDemo;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class ProducerConsumerDemo2 
{
	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();

	}
}

/*
Lock:替代了Synchronized
	lock 
	unlock
	newCondition()

Condition:替代了Object wait notify notifyAll
	await();
	signal();
	signalAll();
*/
class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;

	private Lock lock = new ReentrantLock();

	private Condition condition_pro = lock.newCondition();
	private Condition condition_con = lock.newCondition();



	public  void set(String name)throws InterruptedException
	{
		lock.lock();
		try
		{
			while(flag)
				condition_pro.await();
			this.name = name+"--"+count++;

			System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
			flag = true;
			condition_con.signal();
		}
		finally
		{
			lock.unlock();//释放锁的动作一定要执行。
		}
	}

 
	public  void out()throws InterruptedException
	{
		lock.lock();
		try
		{
			while(!flag)
				condition_con.await();
			System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
			flag = false;
			condition_pro.signal();
		}
		finally
		{
			lock.unlock();
		}
		
	}
}

class Producer implements Runnable
{
	private Resource res;

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

class Consumer implements Runnable
{
	private Resource res;

	Consumer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			try
			{
				res.out();
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
}




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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值