`

java多线程学习总结

阅读更多

第一次在ITeye安家,第一次发帖,希望有个好的开始:

 

最近一段时间在研究java的多线程,在此记录下自己的学习成果,学习过程中参考了大量的博客,

觉得挺好的,都自己做了实验验证了一下,确实挺有意思的,这里感谢他们的研究成果。

 

从最基本的做起:

 

1.继承Thread实现多线程

 

/**
 *   实际上start()方法是创建一个新的线程,而run()只是调用一个单纯的run()方法
 *    如果要在一个实例上产生多个线程就必须用到了另外一种实现方法:实现Runnable接口
 *    因为不能tt.start()两次
 * @author Sa
 *
 */
public class TestThread extends Thread {
	private int x = 0;

	public void run() {

		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep(100);

			} catch (Exception e) {

				e.printStackTrace();
			}
			System.out.println(x++);
		}
	}
	
	public static void main(String[] args) {
		 TestThread tt = new TestThread();
		  tt.start();
//		  tt.start();
		  tt.run();
		

	}

}

 

这个程序有不足的地方,那就是不能产生多个线程多于类TestThread

为了避免这种情况我们看2:

 


2.实现Runnable接口实现多线程

 

如果要产生多个线程我们就引入Runnable接口,如下:

 

 

public class TestRunnable implements Runnable{
	private int x = 0;
	private static int demo = 0;
	
	public   void run() {

		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep(100);
				
			} catch (Exception e) {

				e.printStackTrace();
			}
			System.out.println(x++);
	
		}
		
	}
	
	public static void main(String[] args) {
		 TestRunnable tt = new TestRunnable();
		 Thread t = new Thread(tt);
		 Thread t2 = new Thread(tt);
		  t.start();
		  t2.start();
		  

	}

}

 
这样就可以克服1中的不足,可以产生对于TestRunnable的多线程

但是同样从结果看出,变量不能同步被操作的问题,出现数据的分差

为了解决这种问题引入同步的概念。如下3

 

 

 3.同步代码块和同步方法

 

用同步的方法封装上面2中的代码块,贴上代码:

 

public class TestSynchronized implements Runnable{
	private int x = 0;
	private static int demo = 0;
	
	public synchronized  void run() {

		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep(100);
				
			} catch (Exception e) {

				e.printStackTrace();
			}
			System.out.println(x++);
			synchronized (this) {
				System.out.println("p() is running...,demo = "+demo);
				demo++;
			}
			synchronized (this) {
				System.out.println("q() is running...,demo = "+demo);
				demo--;
			}
			
//			p();
//			q();
			System.out.println("2次运行结束。");
		}
		
		
	}
	public synchronized void p(){
		System.out.println("p() is running...,demo = "+demo);
		demo++;
	}
	
	public synchronized void q(){
		System.out.println("q() is running...,demo = "+demo);
		demo--;
	}
	

	
	public static void main(String[] args) {
		 TestSynchronized tt = new TestSynchronized();
		 Thread t = new Thread(tt);
		 Thread t2 = new Thread(tt);
		  t.start();
		  t2.start();
		  

	}

}

 

注释掉的部分是同步方法,用synchronized( ){  }包住的部分称为同步块,效果是是一样的,我想大家都知道。

 

开始的时候我也不明白synchronized( 参数 )里面的 参数是什么意思,现在说明一下:

参数 的类型是对象,说明同步的时候,这个对象不允许被别的线程使用,当然也不能有多个线程同时执行这块代码(称为同步代码块),对于synchronized关键字,我想说三点:

 

 一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,
       一个时间内只能有一个线程得到执行。
       另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,
      另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。 
 

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,
      其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

    

这也是网上常说的三点。

 

4.线程的等待和唤醒

      线程的等待使用wait()方法,必须在synchronized(){  }块里面使用wait(),否则无效。

      用notify()或notifyAll()来唤醒等待的线程。详细的注意事项我会一会写出来,先来看一下代码

public class ThreadA {
	public static void main(String args[]) {
		ThreadB b = new ThreadB();
		b.start();//b并不是一个线程,b调用start()方法产生一个线程
	
		synchronized (b) {//意思是获取对象b的资源锁,在使用的时候其他线程不允许操作b这个对象
			System.out.println("Waiting for b to complete...");
			try {
				Thread.sleep(3000);
				b.wait();//将当前的资源锁暂时给别的线程
				//可以通过两种方法返回资源锁
				//1.设置借出时间,到时间自动得到资源锁
				//2.让其他线程自动还回来,通过在其他的代码块里面写notify(),or notifyAll();
			} catch (InterruptedException e) {
			}
			System.out.println("Completed.Now back to main thread");
		}
		
		System.out.println("....Total is:" + b.total);
	}

}
/**
 * 这里有一个问题:注释掉notify();  主方法的 线程仍然能够被唤醒
 * 只能猜想:run()方法调用结束后会去检测有没有其他的线程需要调用,如果没有则把资源锁还给原来的主线程,主线程继续执行。
 * 具体这个猜想对不对,可以多做几个线程来测试一下。
 * @author Sa
 *
 */
class ThreadB extends Thread {
	int total = 0;

	public void run() {
		synchronized (this) {//this代表当前对象
			System.out.println("ThreadB is running...");
			for (int i = 0; i < 100; i++) {
				total += i;
			}
			System.out.println("total is " + total);
			// notify();   
			//唤醒正在等待的线程,并不是唤醒了等待的线程自己就会立即结束,
			//而是唤醒之后把自己的任务执行完,被唤醒的线程才会去执行自己的任务.
		}
	}
}

 

这个程序无意在网上看见的,开始不明白作者的意思,理解了一下,大彻大悟,每个语句我都做了注释,我有一个问题:

也想问问大家:注释掉notify();  主方法的 线程仍然能够被唤醒,这是为什么?

 

(我的猜想:

 * 猜想: 如果锁的是thread对象,当wait()以后;
 * 只要在其他线程里面执行了锁定的这个对象的run方法,就会自动返回对象锁,
 * 要是锁的是一般的对象,必须notify(),才能唤醒。

 

我想如果大家明白我的意思的话,不妨问问自己。这个问题我看见网上有人在问,也没有问出个所以然来,再此和大家分享下。

 

5.死锁的模拟和解决

前段时间打算去面试,发现连一个死锁都不会写,实在是很无奈,因为网上一些视频老师说的都是多线程不重要,我是自学的所以也没注意多线程这一块,后来发现要写出死锁现象还必须知道点多线程的知识点,再次和大家分享一下:

 

public class DeadLockDemo implements Runnable{
	
	private boolean flag = false;
	
	static public Integer a = Integer.valueOf(1);
	static public Integer b = Integer.valueOf(2);

	public static void main(String[] args) {
		DeadLockDemo d1 = new DeadLockDemo();
		DeadLockDemo d2 = new DeadLockDemo();
		Thread t1 = new Thread(d1,"线程1");
		Thread t2 = new Thread(d2,"线程2");
		d1.flag = true;
		d2.flag = false;
		t1.start();
		t2.start();
	}
	
	public void run(){
		if(flag){
			System.out.println(Thread.currentThread().getName()+" is running");
			synchronized (a) {
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized (b) {
					System.out.println("b = "+b);
				}
			}
		}else{
			System.out.println(Thread.currentThread().getName()+" is running");
			synchronized (b) {
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized(a){
					System.out.println("a = "+a);
				}
			}
		}
	}

}

 

 

 

死锁自己能写一个这样的出来就差不多了,不必了解太深,对于面试已经足够

 

6.生产者和消费者经典问题

 

这个问题大一的时候就困扰我了,一直忙着找借口不去理解这东西,乘着东风之势,也理解了这个经典的问题,也算是给面试增加自信心吧,再次和大家分享一番,上代码:

 

 

public class ProducerConsumer {

	public static void main(String[] args) {

		ProductStack ps = new ProductStack();

		Producer p = new Producer(ps, "生产者1");

		Consumer c = new Consumer(ps, "消费者1");

		new Thread(p).start();

		new Thread(c).start();

	}

}

class Product {

	int id;
	private String producedBy = "N/A";

	private String consumedBy = "N/A";

	// 构造函数,指明产品ID以及生产者名字。

	Product(int id, String producedBy) {

		this.id = id;

		this.producedBy = producedBy;

	}

	// 消费,需要指明消费者名字

	public void consume(String consumedBy) {

		this.consumedBy = consumedBy;

	}

	public String toString() {

		return "Product : " + id + ", produced by " + producedBy

		+ ", consumed by " + consumedBy;

	}

	public String getProducedBy() {

		return producedBy;

	}

	public void setProducedBy(String producedBy) {

		this.producedBy = producedBy;

	}

	public String getConsumedBy() {

		return consumedBy;

	}

	public void setConsumedBy(String consumedBy) {

		this.consumedBy = consumedBy;

	}
}

// 这个class就是仓库,是生产者跟消费者共同争夺控制权的同步资源

class ProductStack {

	int index = 0;

	Product[] arrProduct = new Product[6];

	// push使用来让生产者放置产品的

	public synchronized void push(Product product) {

		// 如果仓库满了

		while (index == arrProduct.length) // 这里本来可以用if(),但是如果catch

		// exception会出问题,让满的index越界

		{

			try {

				// here, "this" means the thread that is using "push"

				// so in this case it's a producer thread instance.

				// the BIG difference between sleep() and wait() is, once

				// wait(),

				// the thread won't have the lock anymore

				// so when a producer wait() here, it will lost the lock of

				// "push()"

				// While sleep() is still keeping this lock

				// Important: wait() and notify() should be in "synchronized"

				// block

				System.out.println(product.getProducedBy() + " is waiting.");

				// 等待,并且从这里退出push()

				wait();

			} catch (InterruptedException e) {

				e.printStackTrace();

			}

		}

		System.out.println(product.getProducedBy() + " sent a notifyAll().");

		// 因为我们不确定有没有线程在wait(),所以我们既然生产了产品,就唤醒有可能等待的消费者,让他们醒来,准备消费

		notifyAll();

		// 注意,notifyAll()以后,并没有退出,而是继续执行直到完成。

		arrProduct[index] = product;

		index++;

		System.out.println(product.getProducedBy() + " 生产了: " + product);

	}

	// pop用来让消费者取出产品的

	public synchronized Product pop(String consumerName) {

		// 如果仓库空了

		while (index == 0) {

			try {

				// here will be the consumer thread instance will be waiting ,

				// because empty

				System.out.println(consumerName + " is waiting.");

				// 等待,并且从这里退出pop()

				wait();

			} catch (InterruptedException e) {

				e.printStackTrace();

			}

		}

		System.out.println(consumerName + " sent a notifyAll().");

		// 因为我们不确定有没有线程在wait(),所以我们既然消费了产品,就唤醒有可能等待的生产者,让他们醒来,准备生产

		notifyAll();

		// 注意,notifyAll()以后,并没有退出,而是继续执行直到完成。

		// 取出产品

		index--;

		Product product = arrProduct[index];

		product.consume(consumerName);//添加产品消费者的名字

		System.out.println(product.getConsumedBy() + " 消费了: " + product);

		return product;

	}

}

class Producer implements Runnable {

	String name;
	ProductStack ps = null;

	Producer(ProductStack ps, String name) {

		this.ps = ps;

		this.name = name;

	}

	public void run() {

		for (int i = 0; i < 20; i++) {

			Product product = new Product(i, name);

			ps.push(product);

			try {

				Thread.sleep((int) (Math.random() * 200));

			} catch (InterruptedException e) {

				e.printStackTrace();

			}

		}

	}

}

class Consumer implements Runnable {

	String name;
	ProductStack ps = null;

	Consumer(ProductStack ps, String name) {

		this.ps = ps;

		this.name = name;

	}

	public void run() {

		for (int i = 0; i < 20; i++) {

			ps.pop(name);

			try {

				Thread.sleep((int) (Math.random() * 1000));

			} catch (InterruptedException e) {

				e.printStackTrace();

			}

		}

	}

}

 

 

7.JDK新的线程库

jdk5以后提供了一个新的线程库

java.util.concurrent并发包

但是好像现在用的很少,可能是开发中多线程没有想象中用的那么多吧

 

 

8.用新的线程库模拟交通灯系统(传智博客版本共享)

 

这里有一份用新的并发包开发的一套小的模拟系统,我在网上看见的,特地和大家分享:

代码可以直接运行:

 

 

交通灯用枚举类型:

public enum Lamp {
	
	S2N("N2S","S2W",false),S2W("N2E","E2W",false),E2W("W2E","E2S",false),E2S("W2N","S2N",false),
	N2S(null,null,false),N2E(null,null,false),W2E(null,null,false),W2N(null,null,false),
	S2E(null,null,true),E2N(null,null,true),N2W(null,null,true),W2S(null,null,true);
	
	private String opposite;
	private String next;
	private boolean lighted;
	
	private Lamp(String opposite,String next,boolean lighted){
		this.opposite = opposite;
		this.next = next;
		this.lighted = lighted;
	}
	
	public boolean isLighted(){
		return lighted;
	}
	
	public void light(){
		this.lighted = true;
		if(this.opposite!=null){
			Lamp.valueOf(opposite).light();
		}
		System.out.println(name() + " lamp is green,下面总共应该有6个方向能看到汽车穿过!");
	}
	
	public Lamp black(){
		this.lighted = false;
		if(this.opposite!=null){
			Lamp.valueOf(opposite).black();
		}
		Lamp nextLamp = null;
		if(next!=null){
			nextLamp = Lamp.valueOf(next);
			System.out.println("绿灯从" + name() + "-------->切换为" + next);
			nextLamp.light();
		}
		return nextLamp;
	}
}

 

公路类:

 

public class Road {
	
	private String name;
	private List<String> vehicles = new ArrayList<String>();
	
	public Road(String name){
		this.name = name;
		
		//产生车辆用一个线程
		ExecutorService pool =  Executors.newSingleThreadExecutor();
		
		pool.execute(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for(int i = 0 ; i < 1000 ; i++){
					try {
						Thread.sleep((new Random().nextInt(10) + 1)*1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					//每隔1-10s随机产生一辆车
					vehicles.add(Road.this.name+" : 第"+i+"辆车");
					System.out.println(Road.this.name+" : 第"+i+"辆车出现");
				}
			}
		});
		
		//做一个定时器,在绿灯的情况下每隔1秒通车
		ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
		timer.scheduleAtFixedRate(
				new Runnable(){
					public void run(){
						if(vehicles.size()>0){
						boolean lighted = Lamp.valueOf(Road.this.name).isLighted();
						if(lighted){
							System.out.println(vehicles.remove(0)+" is passing");
						}
						}
					}
				},
				1,
				1,
				TimeUnit.SECONDS);
	}
	
}

 

 

 

灯的控制器:

 

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class LampController {
	
	private Lamp currentLamp;
	
	public LampController(){
		currentLamp = Lamp.S2N;
		currentLamp.light();
		//定时器,每隔10秒钟切换红绿灯
		ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
		timer.scheduleAtFixedRate(
				new Runnable() {
					@Override
					public void run() {
						// TODO Auto-generated method stub
						currentLamp = currentLamp.black();
						
					}
				},
				10,
				10,
				TimeUnit.SECONDS);
	}
}

 

 

 

主类(运行主方法即可)

 

public class MainClass {
	
	public static void main(String[] args) {
		
		String []directions = new String[]{"S2N","S2W","E2W","E2S",
				"N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"};
		
		for(int i = 0;i<directions.length;i++){
			new Road(directions[i]);
		}
		
		new LampController();
	}
}

 

 

我大致解释一下:

          马路上穿梭的线路一共有12条,分别是:向左拐的四条,向右拐的有四条,直线四条;

          每条路上用线程随机产生车辆,向右拐的情况不受灯的控制,

          灯为true的时候,代表这个灯控制的两个方向可以通车;

          每隔十秒,会转到下一组灯亮,总共有三组灯,循环亮着,循环通车

 

 

请大家也不要误认为我是在做广告,没那个必要,只是觉得张老师的想法确实很好,特意贴出来,呵呵,希望不要见外。

 

第一次发帖,做的不好还请大家多支持啊,也希望在这里能交到很多好朋友

 

 

 

分享到:
评论
2 楼 1250605829 2012-07-30  
不错唉  很清晰。~~~
1 楼 lghno1 2012-06-18  

相关推荐

    Java 多线程学习总结6

    在“Java多线程学习总结6”这个主题中,我们可以深入探讨Java多线程的实现、管理及优化。下面将详细阐述相关知识点。 1. **线程的创建方式** - **继承Thread类**:自定义类继承Thread类,并重写run()方法,创建...

    corejava多线程学习总结.pdf

    ### CoreJava多线程学习总结 #### 一、基本概念 多线程是现代软件开发中非常重要的一个概念,尤其在Java这样的高级编程语言中,它使得程序能够更高效地利用计算机资源,实现并发处理任务。为了更好地理解CoreJava...

    Java多线程学习总结

    Java多线程是编程中的重要概念,尤其在开发高并发、高性能的应用时不可或缺。本文将深入探讨Java中的线程和进程,以及如何在Java中实现多线程。 首先,理解线程和进程的概念至关重要。线程是操作系统分配CPU时间片...

    Java多线程学习总结.pdf

    Java多线程学习总结.pdf

    java多线程学习总结.docx

    ### Java多线程学习总结 #### 一、Java多线程基本概念 1. **线程状态** - Java中的线程状态分为以下几种:新生(New)、可运行(Runnable)、运行(Running)、等待/阻塞(Waiting/Blocked)以及终止(Terminated...

    Java 多线程学习总结归纳(附代码)

    下面是对Java多线程学习的详细解析。 1. **多线程概述**: 多线程是指一个程序内可以同时执行多个独立的执行流,每个执行流被称为一个线程。Java通过Thread类来代表线程,每个线程都有自己的生命周期,包括新建、...

    Java多线程的总结

    Java多线程是Java编程中的一个核心概念,它在现代软件开发中扮演着至关重要的角色。...通过深入学习和实践上述Java多线程的知识点,开发者能够构建出高效、稳定、可控的多线程程序,满足各种复杂的并发需求。

    java多线程学习资料

    ### Java多线程学习资料知识点解析 #### 一、引言 Java作为一种广泛使用的编程语言,在并发编程领域具有独特的优势。多线程是Java中实现并发处理的核心技术之一,能够显著提升程序的性能和响应性。本文将深入探讨...

    java多线程学习-ftp上传

    总结来说,Java多线程学习和FTP上传结合,可以帮助我们构建高效、可控的文件上传服务。通过线程池,我们可以更好地管理并发任务,优化资源使用,提高FTP上传的性能。学习这些内容对于Java开发者尤其重要,尤其是在...

    Java线程学习和总结

    本文档("Java线程学习和总结.htm")可能包含了更多关于线程的实例、源码分析和常见问题解决方案,你可以通过阅读来进一步加深对Java线程的理解。同时,"Java线程学习和总结.files"目录下的文件可能是与文章相关的...

    JAVA多线程编程技术PDF

    总结起来,“JAVA多线程编程技术PDF”涵盖了多线程的基本概念、同步机制、线程通信、死锁避免、线程池以及线程安全的集合类等内容。通过深入学习这份资料,开发者可以全面掌握Java多线程编程技术,提升程序的并发...

    Java 多线程学习详细总结

    【Java 多线程学习详细总结】 在Java编程中,多线程是处理并发执行任务的关键技术。本文将深入探讨Java中的多线程概念、实现方式、线程状态转换、线程调度、线程同步以及数据传递等相关知识。 1. **扩展`java.lang...

Global site tag (gtag.js) - Google Analytics