多线程——讲的不错,认认真真做的笔记,认真再看!

Posted AntarcticPenguin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程——讲的不错,认认真真做的笔记,认真再看!相关的知识,希望对你有一定的参考价值。

一、线程和进程

进程可以看成是一个运行中的程序,每个应用就是一个运行的程序,可以看成是一个进程。操作系统会为每个进程分配内存空间和CPU时间等。多任务支持了多进程。
线程成为轻量级的进程,有自己的运行环境。线程存在于进程中,每个进程最少有一个线程,线程分享进程的资源。例如程序中同时进行数据读取和数据处理,这样能够提高效率,这时候就需要两个线程。

二、java中的多线程实现的方式

java中提供了两种实现线程的方式:
  • 通过继承Thread类实现多线程
               1、继承Thread类实现线程类,需要覆盖run方法
                     public class MyThread extends Thread{
                           public void run(){
                            //定义线程要执行的代码
                           }
                     }
               2、通过线程类创建线程对象:
                      Thread t=new MyThread();
                      Thread t=new MyThread("线程的名字");
               3、线程的启动通过start方法实现
看下面这个例子1:
public class ThreadTest {
	public static void main(String[] args) {
		System.out.println("主线程开始运行...");
		PrintNumber pn = new PrintNumber();
		pn.start();//这个就要看系统CPU是否给它执行时间,给就执行不给,不能执行
		PrintLetter pl = new PrintLetter();
		pl.start();
		System.out.println("主线程运行结束...");
	}
}
class PrintNumber extends Thread{//实现线程就要几成Thread类,实现方法
	public void run(){
		for(int i=1;i<1000000;i++){//循环100万次,每一万次输出一个
			if(i%10000==0)
				System.out.println("----"+i);
		}
	}
}
class PrintLetter extends Thread{
	public void run(){
		for(int i=0;i<1000000;i++){
			if(i%10000==0)
			System.out.println((char)(\'a\'+i%26));
		}
	}
}
 
  • 这个结果就是,main主线程开始,然后进行其他的线程,主线程结束,由于系统分配着进行时间,所以两个线程穿插交替进行,可以运行一下体验一下,如图下面:
  • 通过实现Runable接口实现多线程
               1、需要实现run方法
                     public class MyThread2 implements Runable{
                           public void run(){
                            //定义线程要执行的代码
                           }
                     }
               2、创建线程对象:
                      Thread tt=new MyThread(new MyThread2());
               3、线程的启动通过start方法实现
看下面的例子2。
 
public class RunnableTest {
	public static void main(String[] args) {
		System.out.println("主线程开始运行...");
		PrintNumber pn = new PrintNumber();
		Thread t1 = new Thread(pn);
		Thread t3 = new Thread(pn);
		t1.start();
		t3.start();
		PrintLetter pl = new PrintLetter();
		Thread t2 = new Thread(pl);
		t2.start();
		System.out.println("主线程运行结束...");
	}
}
class PrintNumber implements Runnable{
	public void run(){
		for(int i=1;i<1000000;i++){
			if(i%10000==0)
				System.out.println("----"+i);
		}
	}
}
class PrintLetter implements Runnable{
	public void run(){
		for(int i=0;i<1000000;i++){
			if(i%10000==0)
			System.out.println((char)(\'a\'+i%26));
		}
	}
}
 
 
经过实践可以,跟上面那个结果类似

三、线程的名字

  • 通过继承Thread类实现多线程
                     class MyThread extends Thread{
                           public MyThread(String name){
                           super(name);
                           }
                           ......
                     }
  • 通过实现Runable接口实现多线程
                      MyRunnable r=new MyRunnable();
                      Thread t3=new Thread(r,"线程3");
  • 通过Thread对象的setName方法设置
  • 调用getName方法得到线程的名字
  • 例子6.3线程名字的使用
 
public class ThreadNameTest {
	public static void main(String[] args) {
		MyRunnable r = new MyRunnable();
		Thread t1 = new MyThread("线程1");
		Thread t2 = new MyThread();
		t2.setName("线程2");
		Thread t3 = new Thread(r, "线程3");
		Thread t4 = new Thread(r);
		t4.setName("线程4");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

class MyRunnable implements Runnable {
	public void run() {
		for (int i = 1; i < 11; i++)
			System.out.println(Thread.currentThread().getName() + "--" + i);
	}
}

class MyThread extends Thread {
	public MyThread() {
	}

	public MyThread(String name) {
		super(name);
	}

	public void run() {
		for (int i = 1; i < 11; i++)
			System.out.println(getName() + "--" + i);
	}
}
 

四、线程的优先级

  • 默认情况下,一个程序的多个线程具有相同的优先级,也就是获得CPU的概率相同。可以通过设置线程的优先级来调整每个线程获得CPU的机会大小。
  • 调用线程的setPriority方法设置优先级,参数表示优先级。优先级的最小值是1,最大的值是9,默认值是5。
  • 需要设置优先级的情况比较少。
  • 例子(6.4)线程的优先级
 
public class ThreadPriority  implements Runnable{

	public static void main(String[] args) {
		Runnable r1 = new ThreadPriority();
		Runnable r2 = new ThreadPriority();
		Thread thread1 = new Thread(r1,"t1");
		thread1.setPriority(1);
		
		Thread thread2 = new Thread(r2,"t2");
		thread2.setPriority(7);
		thread1.start();
		thread2.start();
	}
	
	public void run(){
		for(int i=0;i<100;i++){
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
}
 

五、让线程等待

根据需要可以让线程等待一段时间再执行,可以通过4种方式:
  • 使用sleep让线程等待一段时间
  • 使用yield方法让线程让出执行机会
  • 使用join让线程等待
  • wait方法与notity一起使用,在后面单独介绍,这里介绍3个
(1)使用sleep方法让线程等待
  • 调用Tread的sleep方法让当前线程等待一段时间,参数指出等待时间,单位为毫秒。sleep方法需要使用try....catch处理异常
例子6.5让线程休息
 
public class ThreadSleep implements Runnable {
	public void run() {
		for (int k = 0; k < 5; k++) {
			if (k == 2) {
				try {
					Thread.sleep(5000);//让它等待了5秒钟,运行结果就是0 1然后等待5秒再出现2 3 4
				} catch (Exception e) {
				}
			}
			System.out.print(" " + k);
		}
	}

	public static void main(String[] args) {
		Runnable r = new ThreadSleep();
		Thread t = new Thread(r);
		t.start();
	}
}
 
 
(2)使用yield方法让出执行权
  • yield方法与sleep方法相似,只是它不能由用户指定线程暂停多长时间。sleep方法可以使低优先级的线程得到执行机会,当然也可以让同优先级和高优先级的线程有执行的机会。而yield方法只能使同优先级的线程有执行的机会。
例子6.7使用yield方法让出执行权
 
public class ThreadYield {
	public static void main(String[] args) {
		Thread t1 = new MyThread();
		t1.setName("线程A");
		Thread t2 = new MyThread2();
		t2.setName("线程B");
		t1.start();
		t2.start();
	}
}
class MyThread extends Thread{
	public void run(){
		for(int i=0;i<100;i++){
			Thread.yield();
			Thread.yield();
			System.out.println(Thread.currentThread().getName()+i);
		}
	}
}
class MyThread2 extends Thread{
	public void run(){
		for(int i=0;i<100;i++){
			System.out.println(Thread.currentThread().getName()+i);
		}
	}
}
 
 
 
(3)使用join方法让某个线程先执行完
  • 可以使用join方法让某个线程执行完之后再执行另外一个线程
例子6.8(没有使用join)——输出结果0
public class ThreadJoin extends Thread{
	public static int a = 0;

	public void run() {
		for (int k = 0; k < 5; k++) {
			a = a + 1;
		}
	}

	public static void main(String[] args) {
		Runnable r = new ThreadJoin();
		Thread t = new Thread(r);
		t.start();
		System.out.println(a);
	}
}
 
例子6.9(使用join)——输出结果5
 
public class ThreadJoin extends Thread{
	public static int a = 0;

	public void run() {
		for (int k = 0; k < 5; k++) {
			a = a + 1;
		}
	}

	public static void main(String[] args) {
		Runnable r = new ThreadJoin();
		Thread t = new Thread(r);
		t.start();
		try {
			t.join();
		} catch (InterruptedException e) {			
		}
		System.out.println(a);
	}
}
 
 

六、实例:实现人能够同时说话和开车

  • 编写People类表示人,People类具有说话(speak)和开车(Drive)的功能,让Person类支持多线程,即能够在开车的同时说话
 
public class Person implements Runnable {

	int speakNo = 0;
	int driveNo = 0;

	private boolean canStop = false; // 是否停止线程

	public static void main(String[] args) {
		Person person = new Person();

		Thread t1 = new Thread(person, "speak"); // 第二个参数给出线程的名字
		Thread t2 = new Thread(person, "drive");

		t1.start();
		t2.start();

		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		person.setCanStop(true);
	}

	public void run() {
		while (true) {
			String name = Thread.currentThread().getName();
			if (name.equals("speak")) {
				speak();
			} else {
				drive();
			}
			if (canStop) {
				break;
			}
		}
	}

	public void drive() {
		out.println("正在--------------开车!" + driveNo++);
		try {
			Thread.sleep(5);
		} catch (InterruptedException e) {
		}
	}

	public void speak() {
		out.println("正在说话!" + speakNo++);
		try {
			Thread.sleep(5);
		} catch (InterruptedException e) {
		}
	}

	public boolean isCanStop() {
		return canStop;
	}

	public void setCanStop(boolean canStop) {
		this.canStop = canStop;
	}
}
 
 

七、资源同步

多线程的问题
 
...
int tickets = getTicket();      (1)
setTickets(tickets-1);       (2)
...
假设A线程和B线程分别表示两个售票窗口,可能的执行过程如下:
A线程执行(1),B线程执行(1),A线程执行(2),B线程执行(2),
资源同步可以使用关键词synchronized,相当于对资源加锁,加锁之后其他代码就不能访问了,只有等当前代码执行完之后并解锁,其他的代码才能访问。synchronized可以用在对象上,也可以用在方法上,也可以用在一段代码上。
例子:
6.11在对象上加锁
public class SynchronizeDemo {

	public static void main(String[] args) {
		TicketManager tm = new TicketManager();
		Thread t1 = new Seller(tm, "A窗口");
		Thread t2 = new Seller(tm, "B窗口");
		t1.start();
		t2.start();
	}
}

class Seller extends Thread {
	TicketManager tm;

	public Seller(TicketManager tm, String name) {
		super(name);
		this.tm = tm;
	}

	public void run() {
		while (true) {
			synchronized (tm) {
				int temp = tm.getCount();
				if (temp == 0)
					break;
				temp--;
				System.out.println(Thread.currentThread().getName()
						+ "卖了一张票!还剩下" + temp + "张票!");
				tm.setCount(temp);
				try {
					sleep(100);
				} catch (InterruptedException e) {
				}
			}
		}
	}
}

class TicketManager {
	int count = 100;

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}
}
 
6.12在方法上加锁
public class SynchronizeDemo {

	public static void main(String[] args) {
		Manager manager = new Manager();
		Thread t[] = new Thread[10];
		for(int i=0;i<5;i++){
			t[i] = new Person(manager,"第"+(i+1)+"个人");
			t[i].start();
		}
	}
}

class Person extends Thread {
	Manager manager;

	public Person(Manager manager, String name) {
		super(name);
		this.manager = manager;
	}

	public 	void run() {
		for(int i=0;i<10;i++) {
			manager.eat();
			try {
				sleep(100);
			} catch (InterruptedException e) {
			}
		}
	}
}

class Manager {
	public synchronized void eat(){
		String threadName = Thread.currentThread().getName(); 
		System.out.println(threadName+":用刀");
		System.out.println(threadName+":----用叉");
	}
}
 

八、wait和notify

某个线程在执行的过程中发现需要的资源不可用的时候,就需要等待,调用资源的wait方法,让当前线程处于等待状态。
处于等待状态的线程自己不能继续执行,必须等待其它线程唤醒它,其他线程通过notify或者notifyAll方法来唤醒。
例子:生成者的产品要放到仓库中,消费者需要从仓库中消费商品,而仓库中只能存放10件商品。
Producer.java
public class Producer extends Thread{
	private Store store;
	private boolean canStop = false;
	public void setCanStop(boolean canStop) {
		this.canStop = canStop;
	}
	public Producer(Store store,String name){
		super(name);
		this.store = store;
	}
	
	public void run() {
		while(true){
			if(canStop)
				break;
			synchronized(store){
				// 等待仓库有空位置
				while(store.getQuantity() == 10){
					try{
						store.wait();
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
				}
				
				// 生产产品
				String threadName = Thread.currentThread().getName();
				out.println(threadName + "生产了一个商品!");
				store.put();
			
				// 唤起其他等待者
				store.notifyAll();
			}
			
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}			
		}
	}
}
 
Consumer.java
public class Consumer extends Thread{
	private Store store;
	private boolean canStop = false;
	public void setCanStop(boolean canStop) {
		this.canStop = canStop;
	}

	public Consumer(Store store,String name){
		super(name);
		this.store = store;
	}
	
	public void run() {
		while(true){
			if(canStop)
				break;
			synchronized(store){
				// 得到仓库有空位置
				while(store.getQuantity() == 0){
					try{
						store.wait();
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
				}
				
				// 消费
				String threadName = Thread.currentThread().getName();
				out.println(threadName + "消费了一个商品!");
				store.get();
				store.notifyAll();
			}
			
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}			
		}
	}
}
 
Store.java
public class Store {
	/*
	 * 表示库存,quantity的值会影响生产者和消费者,
	 * 有产品才可以消费,仓库没有满才可以生产
	 * 初始为0,最大为10
	 */
	private int quantity = 0;

	public int getQuantity() {
		return quantity;
	}

	public void put() {
		quantity++;
	}	
	
	public void get(){
		quantity--;
	}
}
 
 
Main.java
public class Main {

	public static void main(String[] args) {
		Store store = new Store();
		Producer t1 = new Producer(store, "producer1");
		Producer t2 = new Producer(store, "producer2");
		Consumer t3 = new Consumer(store, "consumer1");
		Consumer t4 = new Consumer(store, "consumer2");

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

		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		t1.setCanStop(true);
		t2.setCanStop(true);
		t3.setCanStop(true);
		t4.setCanStop(true);
	}
}
 

以上是关于多线程——讲的不错,认认真真做的笔记,认真再看!的主要内容,如果未能解决你的问题,请参考以下文章

字典训练算法之KSVD学习

读书笔记 - 《逆转》

多线程三分钟就可以入个门了!

学习liunx计划书

认真准备

9/5课堂笔记及感受 赵世鹏