多线程

程序:是一个指令的集合。

进程:正在执行中的程序,是一个静态的概念。

线程:是进程中的一个单一的连续控制流程,线程又本称为轻量级进程。

一个进程可拥有多个并行的线程,一个进程中的线程共享相同的内存单元,内存地址空间,可以访问相同的变量和对象,而且他们从同一堆中分配对象,通信,数据交换,同步操作。

由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便,而且信息传递速度也更快。

java多线程实现的方法有两种。

第一种:

继承Thread类,重写run方法,创建对象,调用start方法启动多线程。

第二种:

实现Runnable接口,重写run方法,创建对象,调用start方法启动多线程。

实现Runnable接口的类不能直接调用start方法,需要创建一个Thread对象,然后把对象传进去,在调用start方法。

继承Thread类开启多线程:

注意一点,在开启多线程的时候需要调用start,不能直接调用run方法,因为在开启多线程前,需要一些准备工作,这些工作就需要start来做,start在做完这些准备工作后在去调用run方法。

/**
 * 使用多线程打印到1-100
 * 
 * @author FengYuan
 *
 */
public class Test extends Thread {
	/*
	 * 继承Thread类然后重写run方法
	 * 
	 * @see java.lang.Thread#run()
	 */
	@Override
	public void run() {
		for (int i = 0; i <= 100; i++) {
			System.out.println("线程:" + i);
		}
	}

	public static void main(String[] args) {
		Test t = new Test();
		t.start();// 注意多线程的开启需要调用start,不能直接调用run。
		for (int i = 0; i <= 100; i++) {
			System.out.println("main方法::" + i);
		}
	}
}

实现Runnable接口开启多线程

实现Runnable接口不能直接调用start方法,因为start方法属于Thread的,所以需要new一个Thread然后把对象传进去,通过Thread调用start方法

/**
 * 使用多线程打印到1-100
 * 
 * @author FengYuan
 *
 */
public class Test implements Runnable {
	/*
	 * 实现Runnable接口然后重写run方法
	 * 
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		for (int i = 0; i <= 100; i++) {
			System.out.println("线程:" + i);
		}
	}

	public static void main(String[] args) {
		Test t = new Test();
		new Thread(t).start(); // 注意实现Runnable接口对象不可以直接调用start。
		for (int i = 0; i <= 100; i++) {
			System.out.println("main方法::" + i);
		}
	}
}

常见的方法:

sleep 睡眠    让线程睡眠一定的时间。它有两种睡眠时间,一种是毫秒,一种是纳秒

/**
 * 多线程
 * 
 * @author FengYuan
 *
 */
public class Test extends Thread {
	/*
	 * 继承Thread类然后重写run方法
	 * 
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		try {
			sleep(1000); // 让线程睡眠1秒,1000毫秒等于一秒
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		for (int i = 0; i <= 100; i++) {
			try {
				sleep(0, 1000); // 每打印一次让线程睡眠1000纳秒,当然速度太快,看不出效果。
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("线程:" + i);
		}
	}

	public static void main(String[] args) {
		Test t = new Test();
		t.start(); // 注意实现Runnable接口对象不可以直接调用start。
		for (int i = 0; i <= 100; i++) {
			System.out.println("main方法::" + i);
		}
	}
}

 join插队     任何一个线程在调用join后,那么他就有优先执行的权利,其他的线程都要等到调用join方法的线程执行完毕,才可以执行。

通过上面的代码可以看出run方法在执行的是后遇到了sleep所以睡眠了,这是main继续执行完毕,但是在调用join后main方法就只有等到run方法执行完毕才能继续执行

/**
 * 多线程
 * 
 * @author FengYuan
 *
 */
public class Test extends Thread {
	/*
	 * 继承Thread类然后重写run方法
	 * 
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		try {
			sleep(1000); // 让线程睡眠1秒,1000毫秒等于一秒
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		for (int i = 0; i <= 100; i++) {
			try {
				sleep(0, 1000); // 每打印一次让线程睡眠1000纳秒,当然速度太快,看不出效果。
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("线程:" + i);
		}
	}

	public static void main(String[] args) {
		Test t = new Test();
		t.start(); // 注意实现Runnable接口对象不可以直接调用start。
		try {
			t.join(); // 调用join
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		for (int i = 0; i <= 100; i++) {
			System.out.println("main方法::" + i);
		}
	}
}

yield让步    正在执行的线程给给另一个线程让一下,然后继续执行,只是让了一步。

调用方法:

Thread.yield();

wait 等待  然正在执行的线程进入等待,需要被唤醒,没有唤醒将会一直等待下去,不会自己醒来。

使用方法:

t.wait();

finally 唤醒等待的线程  

finallyAll唤醒所有等待的线程。

t.finally(); // 唤醒一个等待的线程
t.finallyAll(); // 唤醒所有等待的线程

下面给一个多线程使用的例子:

现有100张火车票,让5个人去卖票,直到卖完为止。

代码如下:

/**
 * 多线程(卖火车票)
 * 
 * @author FengYuan
 *
 */
public class Test implements Runnable {
	int ticket = 100; // 100张火车票
	/*
	 * 实现Runnable接口然后重写run方法 因为火车票共享所以必须用Runnable
	 * 
	 * @see java.lang.Runnable#run()
	 */

	@Override
	public void run() {
		// 获取当前线程的名称
		String name = Thread.currentThread().getName();
		if (ticket > 0) {
			System.out.println(name + "卖出的第" + ticket + "张票");
			ticket--; // 每卖出一张减1
		} else {
			System.out.println("票买完了");
		}
	}

	public static void main(String[] args) {
		Test t = new Test();
		for (int i = 0; i <= 22; i++) {
			new Thread(t, "1号").start();
			new Thread(t, "2号").start();
			new Thread(t, "3号").start();
			new Thread(t, "4号").start();
			new Thread(t, "5号").start();
		}
	}
}

从上面的代码运行后我们可以看出卖的票有好多重复的,这就好比多个人同时买到的一张火车票,这样的话是不是存在很大的问题,所以有给出了一个方法,可以避免这种情况的发生。

synchronize线程锁,给线程加一把锁,在看效果。

代码如下:

/**
 * 多线程(卖火车票)
 * 
 * @author FengYuan
 *
 */
public class Test implements Runnable {
	int ticket = 100; // 100张火车票
	/*
	 * 实现Runnable接口然后重写run方法 因为火车票共享所以必须用Runnable
	 * 
	 * @see java.lang.Runnable#run()
	 */

	@Override
	public synchronized void run() {
		// 获取当前线程的名称
		String name = Thread.currentThread().getName();
		if (ticket > 0) {
			System.out.println(name + "卖出的第" + ticket + "张票");
			ticket--; // 每卖出一张减1
		} else {
			System.out.println("票买完了");
		}
	}

	public static void main(String[] args) {
		Test t = new Test();
		for (int i = 0; i <= 22; i++) {
			new Thread(t, "1号").start();
			new Thread(t, "2号").start();
			new Thread(t, "3号").start();
			new Thread(t, "4号").start();
			new Thread(t, "5号").start();
		}
	}
}

加锁之后的代码我们再次运行就会发现,票没有重复的了。

这是因为在线程没有上锁的情况下,就会有多个线程进同时进入方法,这样就会造成重复,而枷锁之后,线程每次只能执行一个,一个线程执行完后才能进入下一个线程。