java基础总结——线程(二)

参考资料

概念

http://www.runoob.com/java/java-multithreading.html

上下文切换

https://www.cnblogs.com/xrq730/p/5186609.html

https://blog.csdn.net/u014527912/article/details/78939781

java多线程的同步锁机制

https://www.cnblogs.com/gavin110-lgy/p/5716421.html

http://www.cnblogs.com/hapjin/p/5452663.html

https://www.cnblogs.com/lwbqqyumidi/p/3821389.html

线程生命周期

https://www.cnblogs.com/lwbqqyumidi/p/3804883.html

https://www.cnblogs.com/lwbqqyumidi/p/3817517.html

https://blog.csdn.net/Yoryky/article/details/78515818

https://www.cnblogs.com/sunddenly/p/4106562.html

知识点

  1. 线程和进程的基本概念
  2. 线程的生命周期
  3. 线程的创建和启动
  4. 守护线程和用户线程
  5. 线程的调度和优先级
  6. 上下文切换
  7. 线程同步

线程同步

概念

举一个例子:某餐厅的卫生间很小,几乎只能容纳一个人如厕。为了保证不受干扰,如厕的人进入卫生间,就要锁上房门。我们可以把卫生间想 象成是共享的资源,而众多需要如厕的人可以被视作多个线程。假如卫生间当前有人占用,那么其他人必须等待,直到这个人如厕完毕,打开房门走出来为止。这就 好比多个线程共享一个资源的时候,是一定要分出先来后到的。有人说:那如果我没有这道门会怎样呢?让两个线程相互竞争,谁抢先了,谁就 可以先干活,这样多好阿?但是我们知道:如果厕所没有门的话,如厕的人一起涌向 厕所,那么必然会发生争执,正常的如厕步骤就会被打乱,很有可能会发生意想不到的结果,例如某些人可能只好被迫在不正确的地方施肥……

由此可知,正是因为有这道门,任何一个单独进入如厕的人都可以顺利的完成他们的如厕过程,而不会被干扰,甚至发生以外的结果。这就是说,如厕的时候要讲究先来后到。

上面这个例子,就是典型的 同步案例,一旦第一位开始如厕,则第二位必须等待第一位结束,才能开始他的如厕过程。一个线程,一旦占用某一资源,其他线程必须等待其使用完资源并释放,才能开始占用。这里,最关键的就是卫生间的门。其实,卫生间的门担任的是资源锁的角色,只要如厕的人锁上门,就相当于获得了这个锁,而当他打开锁出来以后,就相当于释放了这个锁。

由此可知,多线程的线程同步机制实际上是靠锁的概念来控制的。而java的锁实际上用对象锁来实现。当虚拟机装载一个class文件的时候,它就会创建一个java.lang.Class类的实例。当锁住一个对象的时候,实际上锁住的是那个类的Class对象。

同步锁synchronized

java引入了对象互斥锁的概念,保证共享数据操作的完整性。每个对象都对应于一个可称为“互斥锁”的标记,这个标记保证在一段时间内,只能有一个线程访问该对象。

关键字synchronized与对象的互斥锁联系。当一个对象用synchronized修饰时,表明该对象在任一时刻只能有一个线程访问。在一个线程访问完后,另一个线程才能访问。

synchronized用法

写法一

synchronized(Obj){
    ......
}

写法二


synchronized void functionName(){
    ......
}

synchronized锁住的是对象。那么这里的对象指的是哪个对象呢?我们可以通过如下示例体现。

  • 示例1
public class ThreadTest0201 extends Thread {
	private String threadName;

	public ThreadTest0201(String threadName) {
		super(threadName);
		this.threadName = threadName;
	}

	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		for (int idx = 0; idx < 10; idx++) {
			new ThreadTest0201(idx + "").start();
			Thread.sleep(1);
		}
	}

	public synchronized void run() {
		for (int idx_i = 0; idx_i < 100; idx_i++) {
			System.out.println("线程名:" + threadName + ";序号" + idx_i);
		}
	}
}

这个程序是让10个线程在控制台上数数,我们希望前一个线程数完后,下一个线程才能继续数,但经过运行发现,虽然ThreadTest0201的run()上加了同步锁,但结果还是乱的。这是因为synchronized加在成员方法上,这实际上是以这个成员方法所在的对象本身作为对象锁。而例程中每个线程都新建了对象并持有相应的对象锁。这必然不能产生同步的效果。

如果要对这些线程进行同步,那么这些线程所持有的对象锁应当是共享且唯一的!要达到希望的效果,可以看如下示例。

  • 示例2
public class ThreadTest0202 extends Thread {
	private String threadName;
	private Object lock;

	public ThreadTest0202(String threadName, Object lock) {
		super(threadName);
		this.threadName = threadName;
		this.lock = lock;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		Object lock = new Object();
		for (int idx = 0; idx < 10; idx++) {
			new ThreadTest0202((idx + ""), lock).start();
			Thread.sleep(1);
		}
	}

	public void run() {
		synchronized (lock) {
			for (int idx_i = 0; idx_i < 100; idx_i++) {
				System.out.println("线程名:" + threadName + ";序号" + idx_i);
			}
		}
	}
}

在启动线程之前,创建了一个Object类实例,并把这个实例通过构造方法传给线程类的私有变量lock。将原来run方法前的synchronized关键字去掉,换用了run方法中的一个synchronized块来实现。这个同步块的对象锁,就是main方法中创建的那个Object对象。由此可知,这10个线程类的lock指向的是同一个对象。运行后我们达到了希望的效果。

由示例3可知,对于同步静态方法,对象锁就是该静态方法所在的类的Class实例。

  • 示例3
public class ThreadTest0203 extends Thread {
	private String threadName;
	
	public ThreadTest0203(String threadName){
		this.threadName = threadName;
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		for(int idx = 0;idx < 10;idx++){
			(new ThreadTest0203(idx + "")).start();
		}
	}

	public synchronized static void counter(String threadName){
		for (int idx_i = 0; idx_i < 100; idx_i++) {
			System.out.println("线程名:" + threadName + ";序号" + idx_i);
		}
	}
	
	public void run(){
		counter(threadName);
	}
}

示例4说明了不同线程调用同一类的不同加有synchronized锁的方法。因为synchronized关键字锁住的是当前对象,所以线程a和b尽管调用的MyTask的不同方法,但仍然是同步的,一个线程执行完后,另一个线程才能执行。

  • 示例4
public class ThreadTest0204 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		MyTask task1 = new MyTask();
		MyTask task2 = new MyTask();
		
		Thread a = new ThreadMana1(task1);
//		Thread b = new ThreadMana1(task1);
		Thread b = new ThreadMana1(task2);
		
		a.start();
		b.start();
	}

}


class MyTask{
	public synchronized void methodA() throws Exception{
		for(int i = 0;i < 50;i++){
			Thread.sleep(100);
			System.out.println("方法:A;序号:" + i);
		}
	}
	
	public synchronized void methodB() throws Exception{
		for(int i = 0;i < 50;i++){
			Thread.sleep(100);
			System.out.println("方法:B;序号:" + i);
		}
	}
}

class ThreadMana1 extends Thread{
	private MyTask task;
	public ThreadMana1(MyTask task){
		this.task = task;
	}
	public void run(){
		try {
			super.run();
			task.methodA();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

class ThreadMana2 extends Thread{
	private MyTask task;
	public ThreadMana2(MyTask task){
		this.task = task;
	}
	public void run(){
		try {
			super.run();
			task.methodB();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

synchronized小结

  1. 如果采用method级别的同步,则对象锁即为method所在的对象,如果是静态方法,对象锁即指method所在的Class对象(唯一);
  2. 对于代码块,对象锁即指synchronized(abc)中的abc;
  3. 在示例1中,对象锁即为每一个线程对象,因此有多个,所以同步失效,示例2共用同一个对象锁lock,因此同步生效,示例3因为是static因此对象锁为ThreadTest0203的class 对象,因此同步生效。
  4. 如果是同步代码块,则对象锁需要编程人员自己指定,一般有些代码为synchronized(this)只有在单态模式才生效;(本类的实例有且只有一个);如果是同步方法,则分静态和非静态两种 。静态方法则一定会同步,非静态方法需在单例模式才生效,推荐用静态方法(不用担心是否单例)。
  5. 如果一个类中有多个synchronized修饰的同步方法,且多个线程持有该类的同一个对象(该类的相同的对象),尽管它们调用不同的方法,各个方法的执行也是同步的。

死锁

以下示例是产生死锁的例程

  • 示例
package indi.thread.part01;

public class MainTest05 implements Runnable {
	public int flag = 1;
	static Object o1 = new Object(), o2 = new Object();

	public void run() {
		System.out.println("flag=" + flag);
		if (flag == 1) {
			synchronized (o1) {
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized (o2) {
					System.out.println("1");
				}
			}
		}
		if (flag == 0) {
			synchronized (o2) {
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized (o1) {
					System.out.println("0");
				}
			}
		}
	}

	public static void main(String[] args) {
		MainTest05 td1 = new MainTest05();
		MainTest05 td2 = new MainTest05();
		td1.flag = 1;
		td2.flag = 0;
		Thread t1 = new Thread(td1);
		Thread t2 = new Thread(td2);
		t1.start();
		t2.start();
	}
}

生产者消费者问题

问题描述

以生产消费馒头为例:有n个人在做馒头,有m个人在吃馒头,有个容量为c的篓子,做好的馒头放进篓子,要吃馒头从篓子里拿,篓子满了就不做了,篓子空了就不吃了。以下是实现的示例

示例

IStorage

package indi.thread.part03;

public interface IStorage {
	// 生产
	void push(Commodity comm);

	// 消费
	Commodity pop();
}

Storage

package indi.thread.part03;

import java.util.LinkedList;

public class Storage implements IStorage {
	private LinkedList<Commodity> container = new LinkedList();
	private int capacity = 5;
	
	//生产
	public synchronized void push(Commodity comm){
		int size;
		while((size = container.size()) >= capacity){
			try {
				System.out.println("【要生产的产品数量】:1;\t【库存量】:"
					+ container.size() + "\t暂时不能执行生产任务!");
				System.out.println("size:" + size);
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		container.add(comm);
		System.out.println("【新增产品数】:1;产品ID:" + comm.getComID() + "\t【现仓储量为】:" + container.size());
		this.notifyAll();
	}
	
	//消费
	public synchronized Commodity pop(){
		Commodity output = null;
		int size;
		while((size = container.size()) <= 0){
			try {
				System.out.println("【要消费的产品数量】:1;\t【库存量】:"
						+ container.size() + "\t暂时不能执行消费任务!");
				System.out.println("size:" + size);
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		output = container.remove((size - 1));
		System.out.println("【消费产品数】:1;产品ID:" + output.getComID() + "\t【现仓储量为】:" + container.size());
		this.notifyAll();
		return output;
	}
}

Commodity

package indi.thread.part03;
/**
 * 商品类
 * @author 61940
 *
 */
public class Commodity {
	private int comID = 0; 
	private static int maxComID = 0;
	
	public int getComID() {
		return comID;
	}

	public void setComID(int comID) {
		this.comID = comID;
	}

	public Commodity(){
		maxComID++;
		this.comID = maxComID;
	}
}

Producer(生产者)

package indi.thread.part03;

public class Producer extends Thread {
	private Storage currentStorage = null;
	
	public Producer(Storage currentStorage){
		this.currentStorage = currentStorage;
	}
	
	public void run(){
		try {
			currentStorage.push(new Commodity());
			Thread.sleep((long) (Math.random() * 200));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

Consumer (消费者)

package indi.thread.part03;
/**
 * 消费者
 * @author 61940
 *
 */
public class Consumer extends Thread{
	private Storage currentStorage = null;
	
	public Consumer(Storage currentStorage){
		this.currentStorage = currentStorage;
	}
	
	public void run(){
		try {
			currentStorage.pop();
			Thread.sleep((long) (Math.random() * 200));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

测试类

package indi.thread.part03;

public class ProducerCustomerTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Storage storage9100 = new Storage();
		for(int i = 0;i < 10;i++){
			(new Producer(storage9100)).start();
		}
		
		for(int i = 0;i < 20;i++){
			(new Consumer(storage9100)).start();
		}
	}

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值