再说如何进行多线程开发之前,我们先来看看什么是线程,什么又是进程,两者有怎样的关系呢?

  • 程序(Program):

计算机指令的集合,以文件形式存储在磁盘上。即指一段静态代码,静态对象。

  • 进程(Process):

程序的一次动态执行过程, 占用特定的地址空间。在某种程度上进程是相互隔离、独立运行的程序。多任务操作系统将CPU时间动态地划分给每个进程,一次可同时执行多个进程,每个进程独立运行。

程序是静态的,进程是动态的。

  • 线程(Thread):

线程是进程中一个“单一的连续控制流程”  或一段执行路径。一个进程可拥有多个并行的(concurrent)线程。一个进程中的线程共享相同的内存单元或内存地址空间,一个进程中的线程可以访问相同的变量和对象,实现线程间的通信、数据交换、同步操作。

单线程:安全性高,效率低。

多线程:安全性低,效率高。

一、实现多线程

实现多线程,我们依赖一个类Thread,Thread类实现了Runnable接口,创建新执行线程有两种方法。

Thread中有定义了许多方法:

String getName() : 返回线程名;    

void SetName(String name) : 改变线程名称;

Thread(Runnable target):Thread的构造方法,获取Runnable的参数。

static Thread currentThread() :返回当前正在执行的线程对象的引用。

int getPriority() :返回线程的优先级

void setPriority(int newPriority) :更改线程的优先级。 优先级取值范围:1--10

boolean isAlive() :测试线程是否处于活动状态。

static void sleep(long millis) :在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。

static void yield() :暂停当前正在执行的线程对象,并执行其他线程。 

  • 方法一

将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。

先看第一步,创建一个Thread的子类并重写run方法:

class MyThread extends Thread {
	@Override
	public void run() {
		for (int i = 1; i <= 100; i++)
			System.out.println(getName() + ":" + i);
	}
}

重写的run方法中描述的是要放在线程中执行的代码块。

接着我们再创建MyThread的对象并启动多个线程:

public class ThreadDemo {

	public static void main(String[] args) {
		//创建线程对象
		MyThread th = new MyThread();
		MyThread th2 = new MyThread();

		//为线程对象改名
		th.setName("A");
		th2.setName("B");

		//start方法启动线程
		th.start();
		th2.start();
	}

}
  • 方法二

创建线程的另一种方法是:声明实现 Runnable 接口的类,该类实现 run 方法。创建该类的实例,在创建 Thread 时将该实例作为一个参数来传递并启动。 

首先我们先创建一个实现Runnable接口的类并实现run方法:

class MyThread2 implements Runnable {

	@Override
	public void run() {
		for (int i = 1; i <= 100; i++)
                        //这里使用Thread的静态方法currentThread()获取当前线程,再调用线程的getName()方法获取线程的名字
			System.out.println(Thread.currentThread().getName() + ":" + i);
	}

}

然后创建这个类的实例,并将其作为参数传入新创建的Thread对象中去,最后启动线程:

public class ThreadDemo {

	public static void main(String[] args) {
		// 创建线程实例,共享一个Runnable对象,如果有成员变量,其中的成员变量相同
		MyThread2 mt = new MyThread2();
		
		//创建 Thread 时将MyThread2的实例作为一个参数来传递
		Thread t = new Thread(mt);
		t.setName("A");
		Thread t2 = new Thread(mt);
		t2.setName("B");

		// 启动线程
		t.start();
		t2.start();
	}

 

二、多线程之间的同步方法

多线程虽然提高了程序的运行效率,同时也使得程序容易出现错误,安全性降低。当一个程序中有多个共享同一数据的线程时,这些线程并发访问这些共享的数据容易造成数据异常。

例如,出售火车票时,有多个窗口,我们把每一个窗口当作一个线程。如果我们不作任何处理,当票还剩最后一张时,线程A进来了,但此时它并没有立刻把票卖出去,而是去做别的事情。就在这个时候,线程B过来了,它一看票还剩最后一张也进来了,然后接着去做别的事情。这时线程A回来并把票卖出去了,此时票全卖完了,也就是0张票,之后再有线程来时发现票数<1就进不来了。但是,线程B已经获取了票的资源,当它处理完其他事情回来卖票时,票数又少了一张,即火车票成了-1张。而这就是线程并发访问时出现的问题。

怎么解决呢,我们需要一把锁,当一个线程进来获取资源之后,我们让它把锁锁上,这样当别的线程再来获取资源时,发现上锁了也就没法进来了,只好等上一个线程把锁释放之后才能进去获取资源。

那么在Java中我们要怎么实现呢,这是我们需要用一个关键字:synchronized。

synchronized:同步并修饰代码块或方法,被修饰的部分一旦被线程访问则直接锁死,防止其他线程访问。

同步代码块的实现方式:

synchronized(mutex){
    ...
}
//mutex需要被所有线程共享

同步方法的实现方式:

public synchronized void method(){
    ...
}
//非静态同步方法中共用的锁对象是this

public static synchronized void method2(){
    ...
}
//静态方法优先于对象先加载,所以没有this
//静态同步方法中共用的锁对象是当前类的字节码对象

我们来看一个简单的火车票出售同步程序

class TicketThread implements Runnable {

	//预出售的火车票总数
	int tickets = 100;
	//共享的锁对象
	Object obj = new Object();

	@Override
	public void run() {
		//窗口一直开放
		while (true) {
			//同步代码块
			synchronized (obj) {
				//当票数>0时操作
				if (tickets > 0) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + ",tickets=" + tickets--);
				}
			}
		}

	}

}

public class TicketThreadTest {
	public static void main(String[] args) {
		//创建自定义类的对象
		TicketThread tt = new TicketThread();

		//创建三个线程,代表三个窗口,传入自定义类的对象
		Thread t1 = new Thread(tt);
		t1.setName("窗口1");

		Thread t2 = new Thread(tt);
		t2.setName("窗口2");

		Thread t3 = new Thread(tt);
		t3.setName("窗口3");
		
		//启动线程
		t1.start();
		t2.start();
		t3.start();

	}
}

 

三、线程的生命周期

生命周期即一个对象的“生老病死”。线程的生命周期主要是一下几步:

线程生命周期

 

 

 

 

 

 

 

 

posted on 2018-08-01 18:12  七宝嘤嘤怪  阅读(110)  评论(0编辑  收藏  举报