从今天起,在本周末之前,我会整理出java多线程的知识,会搜集网络上的内容及图书馆中的书籍,解决多线程留下的疑惑。

本文会从以下几个方面介绍java多线程:简单介绍线程、线程创建、线程的生命周期、线程控制、线程安全、wait及notify、notifyAll。

以案例代码为主。


一:简单介绍线程

线程是程序运行的基本执行单元。当操作系统(不包括单线程的操作系统,如微软早期的DOS。现在的多数操作系统都是多任务操作系

统,多线程是实现多任务的一种方式)在执行一个程序的时候,会在系统中建立一个进程,在这个进程中,必须至少建立一个线程(即

主线程)来作为这个程序运行的入口点。因此,在操作系统中运行的任何程序至少有一个主线程。

二:线程创建

1.继承Thread类,重写run方法

2.实现Runnable接口,实现run方法

两种方法的区别:使用Thread类在操作多线程的时候无法达到资源共享的目的,而使用Runnable接口实现的多线程操作可以实现资

源共享。

public class Thread1 extends Thread{
	public void run() {
		while(true){
		System.out.println("这是第一个线程!");
		}
	}
}
public class Thread2 implements Runnable{
	public void run() {
		while(true){
			System.out.println("这是第二个线程!");
			}
	}
}
public class GO {
	public static void main(String[] args) {
		Thread1 t1 = new Thread1();
		Thread2 t2 = new Thread2();
		t1.start();
		new Thread(t2).start();
	}

}

三:线程的生命周期

线程存在以下5中状态

1.新建new出Thread类的对象

2.就绪调用start()方法,准备就绪,等待cpu进行调度

3.运行执行了run()方法

4.阻塞因为某些原因(如IO阻塞、睡眠、等待、因为所需要的对象被锁定……)暂停执行,可能将资源交给其他资源使用

5.死亡线程执行完毕,不在进行使用。(run()执行完毕)
四:线程的控制

1.join线程(夹三)让第二个线程执行,执行完毕后,再执行第一个线程。


public class TestJoin {
	public static void main(String[] args) {
		MainThread mt = new MainThread();
		mt.start();
	}

}

class MainThread extends Thread {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		JoinThread join = new JoinThread();
		join.start();
		join.setName("Hello");
		for (int i = 0; i < 50; i++) {
			if (i == 10) {
				try {
					join.join();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println(Thread.activeCount());
			System.out.println(Thread.currentThread().getName() + "-----" + i);
		}
	}
}

class JoinThread extends Thread {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i < 50; i++) {
			System.out.println(Thread.currentThread().getName() + "======" + i);
		}
	}
}

2.线程睡眠

Thread.sleep()该方法为静态方法。

该实例是每隔一秒打印当前时间,等5秒后程序终止。

<span style="font-size:14px;">import java.util.Date;

public class TestSleep {
	public static void main(String[] args) {
		TestThread test = new TestThread();
		test.start();
		try {
			test.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		test.interrupt();
	}

}

class TestThread extends Thread {
	@Override
	public void run() {
		while (true) {
			System.out.println("=====" + new Date() + "=====");
			try {
				sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				return;
			}
			
		}
	}
}</span>

3.线程让步

yield()也是静态方法,获得执行机会的线程放弃本次机会,和另外的线程再争夺机会。

public class TestYield {
	public static void main(String[] args) {
		MyThread2 t1 = new MyThread2("A");
		t1.start();
		MyThread2 t2 = new MyThread2("B");
		t2.start();
	}
}
class MyThread2 extends Thread{
	MyThread2(String s){
		super(s);
	}
	@Override
	public void run() {
		for (int i = 0; i <= 100; i++) {
			System.out.println(getName()+"    "+i);
			if(i%10==0){
				yield();
			}
		}
	}
}

特别说明下sleep和yield的区别:

sleep是抱着cpu睡觉,不放弃执行权力,醒来立即执行

yield放弃执行权力,大家一起重新争夺。·

4.线程优先级问题

不起决定性作用,只是执行的几率不同而已

特别注意:在不同的操作系统上的JVM优先级的数字范围不同,windows上为1--10,而linux上为1--7.这就要求我们在用到的时候,尽量使

用java为我们定义好的优先级。
线程默认优先级是 5,Thread 类中有三个常量,定义线程优先级范围:

static int MAX_PRIORITY            线程可以具有的最高优先级。 

 static int MIN_PRIORITY            线程可以具有的最低优先级。 

 static int NORM_PRIORITY            分配给线程的默认优先级

五:线程安全

同步代码块、同步方法

Synchronized(……){}

这里有个实例,线程不安全测试:前提为一个对象,开启两个线程分别打印字符串“A”……"A""B"……"B"

先定义一个类,此类有方法printString(String s),生成一份该类的对象,开启两个线程调用printString(String s )方法


//不安全可能是有两个V对象,再或者就是一个V对象没加锁
public class TestUnsafe {
	public static void main(String[] args) {
		new Thread(new Runnable() {
			 V v = new V();
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					v.PrintString("AAAAAAAAAA");
				}
			}
		},"A").start();
			new Thread(new Runnable() {
				 V v = new V();
				public void run() {
					// TODO Auto-generated method stub
					while(true){
						v.PrintString("BBBBBBBBBB");
					}
				}
			},"B").start();
	}
	
}
class V{
	public synchronized void PrintString(String s){
		for (int i = 0; i < s.length(); i++) {
			System.out.print(s.charAt(i));
		}
		System.out.println();
	}
}

注:这里存在两个问题导致线程不安全,两个V对象和没加同步监视器

会出现A和B交替出现,说明线程不安全。

我们可以利用同步代码块和同步方法解决此问题。

同步代码块


import com.langsin.base.TestUnsafe.V;

public class TestSynchronizedBlock {
	public static void main(String[] args) {
		TestSynchronizedBlock test = new TestSynchronizedBlock();
		test.go();
	}


	private void go() {
		// TODO Auto-generated method stub
		final V v = new V();
		new Thread(new Runnable() {


			@Override
			public void run() {
				// TODO Auto-generated method stub
				while (true) {
					v.printString("AAAAAAAAAAAAAAAAAAAA");
				}
			}
		}).start();
		new Thread(new Runnable() {


			@Override
			public void run() {
				// TODO Auto-generated method stub
				while (true) {
					v.printString("BBBBBBBBBBBBBBBBBBBB");
				}
			}
		}).start();
	}


	class V {
		
		public void printString(String s) {
			synchronized(this.getClass()){
				for (int i = 0; i < s.length(); i++) {
					System.out.print(s.charAt(i));
				}
				System.out.println();
			}
		}
	}
}




同步方法

import com.langsin.base.TestUnsafe.V;


public class TestSynchronizedBlock {
	public static void main(String[] args) {
		TestSynchronizedBlock test = new TestSynchronizedBlock();
		test.go();
	}


	private void go() {
		// TODO Auto-generated method stub
		final V v = new V();
		new Thread(new Runnable() {


			@Override
			public void run() {
				// TODO Auto-generated method stub
				while (true) {
					v.printString("AAAAAAAAAAAAAAAAAAAA");
				}
			}
		}).start();
		new Thread(new Runnable() {


			@Override
			public void run() {
				// TODO Auto-generated method stub
				while (true) {
					v.printString("BBBBBBBBBBBBBBBBBBBB");
				}
			}
		}).start();
	}


	class V {
		
		public void printString(String s) {
			synchronized(this.getClass()){
				for (int i = 0; i < s.length(); i++) {
					System.out.print(s.charAt(i));
				}
				System.out.println();
			}
		}
	}
}





六:wait、notify

实例:

1.两个线程A和B,线程A先执行,线程B再执行

public class TestWait {
	public static void main(String[] args) {
		TestWait test = new TestWait();
		test.go();
	}

	boolean flag = true;
	V v = new V();
	private void go() {
		// TODO Auto-generated method stub
		new Thread(new Runnable() {

			@Override
			public synchronized void run() {
				// TODO Auto-generated method stub
				while(true){
					v.f1();
				}
			}

		}, "A线程").start();
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					v.f2();
				}
			}
		},"B线程").start();
	}
	class V{
		synchronized void f1(){
			while (flag) {
				try {
					wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName());
			flag = true;
			notifyAll();
		} 
		synchronized void f2(){
			while (!flag) {
				try {
					wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName());
			flag = false;
			notifyAll();
		} 
	}
}

2.三个线程A和B、C,线程A先执行,然后线程B再执行,最后C再执行,然后再依次循环


package com.langsin.base;

public class TestThreeThreadInTurn {
	public static void main(String[] args) {
		TestThreeThreadInTurn test = new TestThreeThreadInTurn();
		test.go();
	}

	V v = new V();

	private void go() {
		// TODO Auto-generated method stub
		new Thread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				while (true)
					v.f1();
			}
		}, "A线程").start();
		new Thread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				while (true)
					v.f2();
			}
		}, "B线程").start();
		new Thread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				while (true)
					v.f3();
			}
		}, "C线程").start();
	}

	int flag = 1;

	class V {
		synchronized void f1() {
			while (flag != 1) {
				try {
					wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			flag = 2;
			notifyAll();
			System.out.println(Thread.currentThread().getName());
		}

		synchronized void f2() {
			while (flag != 2) {
				try {
					wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			flag = 3;
			notifyAll();
			System.out.println(Thread.currentThread().getName());
		}

		synchronized void f3() {
			while (flag != 3) {
				try {
					wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			flag = 1;
			notifyAll();
			System.out.println(Thread.currentThread().getName());
		}
	}
}



3.下面给大家介绍一个生产者和消费者问题

包括以下5个类,

ProductContainer容器,里面有一个linkedList。有最大容量,有往里放和往外拿的方法,单例模式

<p style="background:rgb(240,240,240);"><pre name="code" class="java">import java.util.LinkedList;


public class ProductContainer {
private ProductContainer(){}
private static ProductContainer instance = null;
public static synchronized ProductContainer getInstance(){	if(instance==null){		instance = new ProductContainer();
		}
		return instance;
	} 
	private LinkedList<Product> list = new LinkedList<Product>();
	private final int MAX_SIZE = 10;
	public synchronized void putProduct(Product p){
		while(list.size()>=MAX_SIZE){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		list.add(p);
		System.out.println(Thread.currentThread().getName()+"生产了"+p);
		notifyAll();
	}
	public synchronized Product getProduct(){
		while(list.size()<=0){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		Product p = list.removeLast();
		System.out.println(Thread.currentThread().getName()+"消费了"+p);
		notifyAll();
		return p ;
	}
	public void checkSize(){
	System.out.println("现在的桌子上有:"+list.size()+"个");
}
}





Producer线程,生产者

public class Producer extends Thread {
	private ProductContainer pc = null;
	public Producer(ProductContainer pc){
		this.pc = pc;
	}	
	@Override
		public void run() {
			// TODO Auto-generated method stub
			for (int i = 0; i < 1000; i++) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				Product p = new Product(i,"蛋糕");
				pc.putProduct(p);
			}
		}
}

Customer线程,消费者

<pre name="code" class="java">public class Customer extends Thread{
	private ProductContainer pc = null;
	
	public Customer(ProductContainer pc) {
		this.pc = pc;
	}


	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i < 1000; i++) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			pc.getProduct();
		}
	}


}




Product产品

public class Producer extends Thread {
	private ProductContainer pc = null;
	public Producer(ProductContainer pc){
		this.pc = pc;
	}	
	@Override
		public void run() {
			// TODO Auto-generated method stub
			for (int i = 0; i < 1000; i++) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				Product p = new Product(i,"蛋糕");
				pc.putProduct(p);
			}
		}
}

Go主程序main

public class Go {
	public static void main(String[] args) {
		final ProductContainer pc = ProductContainer.getInstance();
		for (int i = 0; i < 2; i++) {
			new Customer(pc).start();
		}
		for (int i = 0; i < 2; i++) {
			new Producer(pc).start();
		}
		new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					pc.checkSize();
				}
			}
		}).start();
	}
}



必须注意的问题:

1.容器一定要定义容量

2.容器往里添加和删除都是阻塞的。wait、notify  注意wait方法要加synchronized修饰

3.消费者和生产者拿到的必须是一个容器(单例模式)


4.Future提前完成任务

假设有两个方法A和B,A调用B,如果B方法执行起来比较长,那么就会导致A方法的阻塞等待,直到B方法完毕后,A方法才能被返回。类似于购买物品,

先生成订单,然后等产品真正到位,我们再拿订单去换取产品。

有以下几个类和接口

Data接口

public interface Data {
	public String obtainString();
}

Future  

public class Future implements Data {
	private boolean ready;
	private Real real;
	public  synchronized void setReal(Real real){
		if(ready){
			return;
		}
		this.ready = true;
		this.real = real;
		notifyAll();
	}
	@Override
	public synchronized String obtainString() {
		// TODO Auto-generated method stub
		if(!ready){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return real.obtainString();
	}

}

Real 实现了Data接口,实现了ObtainString()方法

public class Real implements Data {
	public Real(){
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public String obtainString() {
		// TODO Auto-generated method stub
		return "Hello,langSin!!!";
	}

}

RequestHandler

public class RequestHandler {
	public Data requestData(){
		final Future future = new Future();
		new Thread(new Runnable() {
			@Override
			public void run() {
				Real real = new Real();
				future.setReal(real);
			}
		}).start();
		return future;
	}
}

Go

public class Go {
	public static void main(String[] args) {
		RequestHandler handler = new RequestHandler();
		Data data1 = handler.requestData();
		Data data2 = handler.requestData();
		System.out.println(data1.obtainString());
		System.out.println("我在做其他事情");
		System.out.println(data2.obtainString());
	}
}