java多线程

1.基本概念

进程:是一个正在执行的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元

线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行

一个进程中至少有一个线程,线程有名称,从0开始,比如:Thread-0Thread-1,主线程的名称为main

获取当前线程名称有两种:

Thread.currentThread() 结果为Thread[Thread-0,5,main],Thread[Thread-1,5,main]

this.getName() 结果为Thread-0Thread-1

Thread.currentThread().getName()  结果为Thread-0Thread-1

 

比如:

JVM启动时会有一个进程java.exe

该进程至少一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中,该进程称之为主线程


拓展:其实更细节说明jvmjvm启动不止一个线程,还有负责垃圾回收机制的线程


2.状态

1. 新建状态(New):新创建了一个线程对象。
2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。


多线程状态的内容转自haung_xw的博客 http://blog.csdn.net/huang_xw/article/details/7316354


3.实现方法

a.继承Thread类

步骤:

1.定义类继承Thread

2.复写Thread类中的run方法。目的:将自定义代码存储在run方法中,让线程运行

3.调用线程的start方法。作用:启动线程和调用run方法

public class Demo extends Thread{
	public void run(){
		for(int i=0;i<5;i++){
			System.out.println("自定义线程"+i);
		}
	}
	public static void main(String[] args) {
		Demo demo = new Demo();
		demo.start();
		for(int i=0;i<5;i++){
			System.out.println("主线程"+i);
		}
	}
}


发现运行结果每一次都不同,因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行,明确一点,在某个时刻,只能有一个程序在运行,(多核除外)cpu在做着快速的切换,以达到看上去是同时运行的效果


简单的卖票程序,多个窗口同时买票,ticketCount用static修饰,让多个线程共享票数

代码示例:

public class Ticket extends Thread{
	private static int ticketCount = 5;	//一般不用static,因为生命周期太长
	public void run(){
		while(true){
			if(ticketCount>0){
				System.out.println(Thread.currentThread().getName()+"..."+ticketCount--);
			}
		}
	}
	public static void main(String[] args) {
		Ticket ticket1 = new Ticket();
		Ticket ticket2 = new Ticket();
		Ticket ticket3 = new Ticket();
		Ticket ticket4 = new Ticket();
		ticket1.start();
		ticket2.start();
		ticket3.start();
		ticket4.start();
	}
}



因为static修饰共享,生命周期太长了,那当把static去掉后,用另一种思考方式,即只有ticket1,并且用start方法启动多次是不是可以呢?

代码示例:

public class Ticket extends Thread{
	private int ticketCount = 5;	
	public void run(){
		while(true){
			if(ticketCount>0){
				System.out.println(Thread.currentThread().getName()+"..."+ticketCount--);
			}
		}
	}
	public static void main(String[] args) {
		Ticket ticket1 = new Ticket();
		//Ticket ticket2 = new Ticket();
		//Ticket ticket3 = new Ticket();
		//Ticket ticket4 = new Ticket();
		ticket1.start();
		ticket1.start();
		ticket1.start();
		ticket1.start();
	}
}
<span style="font-size:14px;">
</span>



start方法调用一个已经启动的线程,系统会抛出 IllegalThreadStateException 异常,因为不可以多次启动一个线程,解决方法就是第二种方法实现Runable接口

b.实现Runable接口

出现的原因:

因为当使用第一种方式即继承Thread类时,而同时又要继承其他类时,则出现所继承现象,而java不支持多继承,所以通过第二种方式,实现Runable接口类完成多线程操作


步骤:

  1. 定义类实现Runable接口

  2. 覆盖Runable接口中的run方法,将线程要运行的代码存放在run方法中

  3. 通过Thread类建立线程对象

  4. Runable接口的子类对象作为实际参数传递给Thread类的构造函数

    为什么要将Runable接口的子类对象要传递给Thread的构造函数,因为,自定义的run方法所需的对象是Runable接口的子类对象,所以要让线程去指定对象的run方法,就必须明确该run方法所属对象

  5. 调用Thread类的start方法开启线程并调用Runable接口子类的run方法

public class Ticket implements Runnable{
	private int ticketCount = 5;
	public void run(){
		while(true){
			if(ticketCount>0){
				System.out.println(Thread.currentThread().getName()+"..."+ticketCount--);
			}
		}
	}
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		Thread ticket1 = new Thread(ticket);
		Thread ticket2 = new Thread(ticket);
		Thread ticket3 = new Thread(ticket);
		Thread ticket4 = new Thread(ticket);
		ticket1.start();
		ticket2.start();
		ticket3.start();
		ticket4.start();
	}
}


上面的结果是顺利的情况下产生的,假如多个线程在执行完 if(ticketCount > 0)语句后线程挂起,即cpu不执行当前线程,也就是不执行打印票数输出语句,则当多个暂挂线程苏醒后,会执行打印语句,从而产生0,-1,-2等错票和重票。为了实现效果,故意采用Thread.sleep()语句让线程睡眠,让多个线程挂起,产生错票效果,代码如下:

public class Ticket implements Runnable{
	private int ticketcount = 5;
	public void run(){	//InterruptedException异常不能抛,因为接受的是接口
		while(true){
			if(ticketcount>0){
				try{
					Thread.sleep(20);
				}catch(InterruptedException e){
				}
				System.out.println(Thread.currentThread().getName()+"..."+ticketcount--);
			}
		}
	}
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		Thread ticket1 = new Thread(ticket);
		Thread ticket2 = new Thread(ticket);
		Thread ticket3 = new Thread(ticket);
		Thread ticket4 = new Thread(ticket);
		ticket1.start();
		ticket2.start();
		ticket3.start();
		ticket4.start();
	}
}


问题的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行一部分,还没有执行完,另一个线程就参与进来执行,导致共享数据错误

 

解决方法:

对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行


Java对于多线程安全问题提供了专业的解决方法,就是同步代码块


4.同步代码块

synchronized(对象){

需要被同步的代码

}

对象如同锁,持有锁的线程可以在同步中执行

没有持有锁的线程即使获得了cpu的执行权,也进不去,因为没有获取锁

 

同步的前提:

1.必须要有两个或者两个以上的线程

2.必须是多个线程使用同一个锁

必须保证同步中只能有一个线程在运行

 

好处:解决了多线程的安全问题

弊端:多个线程需要判断锁,较为浪费资源


代码如下:

public class Ticket implements Runnable{
	private int ticketcount = 5;
	Object obj = new Object();	
	public void run(){	//InterruptedException异常不能抛,因为接受的是接口
		while(true){
			synchronized (obj) {
				if(ticketcount>0){
					try{
						Thread.sleep(20);
					}catch(InterruptedException e){
					}
					System.out.println(Thread.currentThread().getName()+"..."+ticketcount--);
				}
			}
		}
	}
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		Thread ticket1 = new Thread(ticket);
		Thread ticket2 = new Thread(ticket);
		Thread ticket3 = new Thread(ticket);
		Thread ticket4 = new Thread(ticket);
		ticket1.start();
		ticket2.start();
		ticket3.start();
		ticket4.start();
	}
}



5.同步函数

需求:

银行有一个金库

有两个储户分别存300元,每次存储100元,存3

 

明确哪些语句需要同步:

1.明确哪些代码是多线程运行的代码

2.明确共享数据

3.明确多线程运行代码中哪些语句是操作共享数据


既然同步代码块是封装代码,函数也是封装代码,那直接让函数具备同步性(同步函数),同样也能实现效果。

class Bank{
	private int sum;
	//Object object = new Object();
	public synchronized void add(int n){	//可以抛出异常 
		//synchronized (object) {
			sum += n;
			try {
				Thread.sleep(10);//刻意让线程等待
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("sum"+sum);
		//}
	}
}
class Customer implements Runnable{
	private Bank bank = new Bank();
	public void run(){
		for(int i=0;i<3;i++){
			bank.add(100);
		}
	}
}
public class BankDemo{
	public static void main(String[] args) {
		Customer customer = new Customer();
		Thread thread = new Thread(customer);
		Thread thread2 = new Thread(customer);
		thread.start();
		thread2.start();
	}
}


注意*:使用同步函数时要注意函数封装的代码,不能盲目直接把函数加一个synchronized修饰符就以为可以了。

比如下面的例子:

public class Ticket implements Runnable{
	private int ticketcount = 5;
	//Object obj = new Object();	
	public synchronized void run(){	//InterruptedException异常不能抛,因为接受的是接口
		while(true){
			//synchronized (obj) {
				if(ticketcount>0){
					try{
						Thread.sleep(20);
					}catch(InterruptedException e){
					}
					System.out.println(Thread.currentThread().getName()+"..."+ticketcount--);
				}
			//}
		}
	}
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		Thread ticket1 = new Thread(ticket);
		Thread ticket2 = new Thread(ticket);
		Thread ticket3 = new Thread(ticket);
		Thread ticket4 = new Thread(ticket);
		ticket1.start();
		ticket2.start();
		ticket3.start();
		ticket4.start();
	}
}


会发现只有0线程在运行,为什么?

原因:没搞清楚要同步的代码是哪些,run方法中的while(true) 语句不需要同步,当线程0获取锁,进来后就无限循环打印,直至把票数打印完。


解决方法:把要同步的代码单独封装到一个函数里,然后将函数变为同步函数即可。

public class Ticket implements Runnable{
	private int ticketcount = 5;
	public void run(){	//InterruptedException异常不能抛,因为接受的是接口
		while(true){
			show();
		}
		
	}
	public synchronized void show(){
		if(ticketcount>0){
			try{
				Thread.sleep(20);
			}catch(InterruptedException e){
			}
			System.out.println(Thread.currentThread().getName()+"..."+ticketcount--);
		}
	}
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		Thread ticket1 = new Thread(ticket);
		Thread ticket2 = new Thread(ticket);
		Thread ticket3 = new Thread(ticket);
		Thread ticket4 = new Thread(ticket);
		ticket1.start();
		ticket2.start();
		ticket3.start();
		ticket4.start();
	}
}


到这里分析一下锁的问题,前面用同步代码块时使用的锁是Object类的对象obj,而同步函数的锁是什么?

函数需要被对象调用,那么函数都有一个所属对象引用,就是this。所以同步函数使用的锁是this


下面通过程序验证:

通过两个线程来买票,一个线程在同步代码块中,一个线程在同步函数中,都执行买票动作。

public class Ticket implements Runnable{
	private int ticketcount = 5;
	Object obj = new Object();
	boolean flag = true;
	public void run(){	//InterruptedException异常不能抛,因为接受的是接口
		if(flag == true){
			while(true){
				synchronized (obj) {
					if(ticketcount>0){
						try{
							Thread.sleep(20);
						}catch(InterruptedException e){
						}
					System.out.println(Thread.currentThread().getName()+"..."+ticketcount--+"同步代码块");
					}
				}
			}
		}
		else{
			while(true){
				show();
			}
		}
	}
	public synchronized void show(){
		if(ticketcount>0){
			try{
				Thread.sleep(20);
			}catch(InterruptedException e){
			}
			System.out.println(Thread.currentThread().getName()+"..."+ticketcount--+"同步函数");
		}
	}
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		Thread ticket1 = new Thread(ticket);
		Thread ticket2 = new Thread(ticket);
		//Thread ticket3 = new Thread(ticket);
		//Thread ticket4 = new Thread(ticket);
		ticket1.start();
		ticket.flag = false;
		ticket2.start();
		//ticket3.start();
		//ticket4.start();
	}
}


通过结果分析,只有同步函数的线程在运行,原因:主线程中执行完ticket.start()时,启动了线程,但是可能处于临时状态,此时主线程继续执行,把接下来的代码

ticket.flag = false;
ticket2.start();

都执行完后,此时flag=false,所以一直都在同步函数中运行。

解决方法:在ticket.start();代码后加上

try{
Thread.sleep(10);
}catch(Exception e){
}

让主线程睡眠,即可解决。可是会发现输出结果还是有问题,出现了0号票


 原因:同步代码块和同步函数两者用的锁不一样,同步代码块用的锁是obj,而同步函数用的锁是this,所以会出现安全隐患,打印出0号票

解决方法:使用同一把锁this即可。不用obj,因为只有同步代码块中才用对象obj作为锁,而同步函数是默认为this锁。

public class Ticket implements Runnable{
	private int ticketcount = 5;
	Object obj = new Object();
	boolean flag = true;
	public void run(){	//InterruptedException异常不能抛,因为接受的是接口
		if(flag == true){
			while(true){
				synchronized (this) {
					if(ticketcount>0){
						try{
							Thread.sleep(20);
						}catch(InterruptedException e){
						}
					System.out.println(Thread.currentThread().getName()+"..."+ticketcount--+"同步代码块");
					}
				}
			}
		}
		else{
			while(true){
				show();
			}
		}
	}
	public synchronized void show(){	//使用this锁
		if(ticketcount>0){
			try{
				Thread.sleep(20);
			}catch(InterruptedException e){
			}
			System.out.println(Thread.currentThread().getName()+"..."+ticketcount--+"同步函数");
		}
	}
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		Thread ticket1 = new Thread(ticket);
		Thread ticket2 = new Thread(ticket);
		//Thread ticket3 = new Thread(ticket);
		//Thread ticket4 = new Thread(ticket);
		ticket1.start();
		try{
			Thread.sleep(10);
		}catch(Exception e){
		}
		ticket.flag = false;
		ticket2.start();
		//ticket3.start();
		//ticket4.start();
	}
}


6.静态同步函数

静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。

如果把同步代码块中的锁Ticket.class换成其他锁,则会出现0号票
public class Ticket implements Runnable{
	private static int ticketcount = 100;
	Object obj = new Object();
	boolean flag = true;
	public void run(){	//InterruptedException异常不能抛,因为接受的是接口
		if(flag == true){
			while(true){
				synchronized (Ticket.class) {
					if(ticketcount>0){
						try{
							Thread.sleep(20);
						}catch(InterruptedException e){
						}
					System.out.println(Thread.currentThread().getName()+"..."+ticketcount--+"同步代码块");
					}
				}
			}
		}
		else{
			while(true){
				show();
			}
		}
	}
	public static synchronized void show(){	//使用Class对象锁
		if(ticketcount>0){
			try{
				Thread.sleep(20);
			}catch(InterruptedException e){
			}
			System.out.println(Thread.currentThread().getName()+"..."+ticketcount--+"同步函数");
		}
	}
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		Thread ticket1 = new Thread(ticket);
		Thread ticket2 = new Thread(ticket);
		//Thread ticket3 = new Thread(ticket);
		//Thread ticket4 = new Thread(ticket);
		ticket1.start();
		try{
			Thread.sleep(10);
		}catch(Exception e){
		}
		ticket.flag = false;
		ticket2.start();
		//ticket3.start();
		//ticket4.start();
	}
}

7.懒汉式的多线程安全问题

不懂懒汉式,可以参考我另一篇博客 (java单例设计模式)http://blog.csdn.net/manfulleo/article/details/51089712
代码如下:
class Single{  
    private Single(){};  
    private static Single single = null;  
    public static synchronized Single getInstance(){  
        if(single == null){  
            single = new Single();  
        }  
        return single;  
    }  
}  
但是会发现上面的代码不够好,因为不管对象是否为null都要判断锁。为了减少判断锁的次数,把代码优化后如下:
class Single{  
    private Single(){};  
    private static Single single = null;  
    public static Single getInstance(){  
        if(single == null){
        	synchronized (Single.class) {
				if(single == null){  
					single = new Single();  
				}
			}
        }
        return single;  
    }  
}  

8.死锁

代码示例:
class Test implements Runnable{
	private boolean flag;
	Test(boolean flag){
		this.flag = flag;
	}
	public void run(){
		if(flag == true){
			synchronized (MyLock.locka) {
				System.out.println("if locka");
				synchronized (MyLock.lockb) {
					System.out.println("if lockb");
				}
			}
		}
		else{
			synchronized (MyLock.lockb) {
				System.err.println("else lockb");
				synchronized (MyLock.locka) {
					System.out.println("else locka");
				}
			}
		}
	}
}

class MyLock{
	static Object locka = new Object();
	static Object lockb = new Object();
}
public class DeadLockTest{
	public static void main(String[] args) {
		Thread t1 = new Thread(new Test(true));
		Thread t2 = new Thread(new Test(false));
		t1.start();
		t2.start();
	}
}

 

9.线程间通信

多个线程操作同一个资源,但操作的动作不一样。
class Res{
	String name;
	String sex;
}

class Input implements Runnable{
	private Res r;
	Input(Res r) {
		this.r = r;
	}
	public void run(){
		int x = 0;
		while(true){
			if(x == 0){
				r.name = "小强";
				r.sex = "男";
			}
			else{
				r.name = "xaiohong";
				r.sex = "women";
			}
			x = (x+1)%2;
		}
	}
}
	
class Ouput implements Runnable{
	private Res r;
	Ouput(Res r){
		this.r = r;
	}
	public void run(){
		while(true){		
			System.out.println(r.name+"..."+r.sex);
		}
	}
}

public class InputOuputDemo{
	public static void main(String[] args) {
		Res r = new Res();
		Input in = new Input(r);
		Ouput out = new Ouput(r);
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(out);
		t1.start();
		t2.start();
	}
}


输出结果本应该是小强...男和xiaohong...women,但是却出现了问题,原因:没有加锁,注意一点,两个线程都要加锁,锁必须是同一把锁。
改正后的代码如下:
class Res{
	String name;
	String sex;
}

class Input implements Runnable{
	private Res r;
	Input(Res r) {
		this.r = r;
	}
	public void run(){
		int x = 0;
		while(true){
			if(x == 0){
				r.name = "小强";
				r.sex = "男";
			}
			else{
				r.name = "xaiohong";
				r.sex = "women";
			}
			x = (x+1)%2;
		}
	}
}
	
class Ouput implements Runnable{
	private Res r;
	Ouput(Res r){
		this.r = r;
	}
	public void run(){
		while(true){		
			System.out.println(r.name+"..."+r.sex);
		}
	}
}

public class InputOuputDemo{
	public static void main(String[] args) {
		Res r = new Res();
		Input in = new Input(r);
		Ouput out = new Ouput(r);
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(out);
		t1.start();
		t2.start();
	}
}


10.生产者消费者

a.一对一

为了满足这样一个需求就是:Input中复制一次,Ouput就输出一次。

代码示例:
class Res{
	String name;
	String sex;
	boolean flag = false;
}

class Input implements Runnable{
	private Res r;
	Input(Res r) {
		this.r = r;
	}
	public void run(){
		int x = 0;
		while(true){
			synchronized (r) {
				if(r.flag == true){
					try {
						r.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				if(x == 0){
					r.name = "小强";
					r.sex = "男";
				}
				else{
					r.name = "xaiohong";
					r.sex = "women";
				}
				x = (x+1)%2;
				r.flag = true;
				r.notify();
			}
		}
	}
}
	
class Output implements Runnable{
	private Res r;
	Output(Res r){
		this.r = r;
	}
	public void run(){
		while(true){	
			synchronized (r) {
				if(r.flag == false){
					try {
						r.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println(r.name+"..."+r.sex);
				r.flag = false;
				r.notify();
			}
		}
	}
}

public class InputOutputDemo{
	public static void main(String[] args) {
		Res r = new Res();
		Input in = new Input(r);
		Output out = new Output(r);
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(out);
		t1.start();
		t2.start();
	}
}


wait(),notify(),notifyAll()都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁。所以在上述代码中用到r.wait(),且wait会抛出异常,必须处理。

为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作的线程持有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒。也就是说等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的 方法定义Object类中。

将Res类中成员私有,代码优化:
class Res{  
    private String name;  
    private String sex;  
    private boolean flag = false;  
      
    public synchronized void set(String name,String sex){  
        if(flag == true){  
            try {  
                this.wait();  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
        this.name = name;  
        this.sex = sex;  
        flag = true;  
        this.notify();  
    }  
    public synchronized void out(){  
        if(flag == false){  
            try {  
                this.wait();  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
        System.out.println(name+"..."+sex);  
        flag = false;  
        this.notify();  
    }  
}  
  
class Input implements Runnable{  
    private Res r;  
    Input(Res r) {  
        this.r = r;  
    }  
    public void run(){  
        int x = 0;  
        while(true){  
            if(x == 0){  
                r.set("小强", "男");  
            }  
            else{  
                r.set("xiaohong", "women");  
            }  
            x = (x+1)%2;  
        }  
    }  
}  
      
class Output implements Runnable{  
    private Res r;  
    Output(Res r){  
        this.r = r;  
    }  
    public void run(){  
        while(true){      
            r.out();  
        }  
    }  
}  
  
public class InputOutputDemo{  
public static void main(String[] args) {  
	Res r = new Res();  
	new Thread(new Input(r)).start();  
	new Thread(new Output(r)).start();  
	/*  
	Input in = new Input(r);  
	Output out = new Output(r);  
	Thread t1 = new Thread(in);  
	Thread t2 = new Thread(out);  
	t1.start();  
	t2.start(); 
	*/  
	}  
}  

b.多对多

代码示例:
class Res{
	private String name;
	private int count = 1;
	private boolean flag = false;
	
	public synchronized void set(String name){
		if(flag == true){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name+"..."+count++;
		System.out.println(Thread.currentThread().getName()+"...生产者"+this.name);
		flag = true;
		this.notify();
	}
	
	public synchronized void out(){
		if(flag == false){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"......消费者"+this.name);
		flag = false;
		this.notify();
	}
}

class Producer implements Runnable{
	private Res r;
	public Producer(Res r) {
		this.r = r;
	}
	public void run() {
		while(true){
			r.set("+商品+");
		}
	}
}

class Consumer implements Runnable{
	private Res r;
	public Consumer(Res r) {
		this.r = r;
	}
	public void run(){
		while(true){
			r.out();
		}
	}
}


public class ProducerConsumerDemo{
	public static void main(String[] args) {
		Res r = new Res();
		Producer producer = new Producer(r);
		Consumer consumer = new Consumer(r);
		Thread t1 = new Thread(producer);
		Thread t2 = new Thread(producer);
		Thread t3 = new Thread(consumer);
		Thread t4 = new Thread(consumer);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

出现了生产2次,只消费一次的问题

解决方法:用notifyAll()唤醒所有等待的线程

代码示例:
class Res{
	private String name;
	private int count = 1;
	private boolean flag = false;
	
	public synchronized void set(String name){
		while(flag == true){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name+"..."+count++;
		System.out.println(Thread.currentThread().getName()+"...生产者"+this.name);
		flag = true;
		this.notifyAll();
	}
	
	public synchronized void out(){
		while(flag == false){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"......消费者"+this.name);
		flag = false;
		this.notifyAll();
	}
}

class Producer implements Runnable{
	private Res r;
	public Producer(Res r) {
		this.r = r;
	}
	public void run() {
		while(true){
			r.set("+商品+");
		}
	}
}

class Consumer implements Runnable{
	private Res r;
	public Consumer(Res r) {
		this.r = r;
	}
	public void run(){
		while(true){
			r.out();
		}
	}
}


public class ProducerConsumerDemo{
	public static void main(String[] args) {
		Res r = new Res();
		Producer producer = new Producer(r);
		Consumer consumer = new Consumer(r);
		Thread t1 = new Thread(producer);
		Thread t2 = new Thread(producer);
		Thread t3 = new Thread(consumer);
		Thread t4 = new Thread(consumer);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}


为什么要用定义while判断标记呢?
让被唤醒的线程再一次判断标记

为什么定义notifyAll?
因为需要唤醒对方线程
因为只要notify,容易出现只唤醒本方线程的情况。

10.生产者消费者(jdk1.5升级版)

用Lock替代synchronized,用Condition对象中的方法替代wait(),notify(),notifyAll()。
之前使用 synchronized时,有隐式释放锁步骤,而Lock则是用unlock方法显式释放锁。

用新的特性和关键字替换上面的代码:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Res{
	private String name;
	private int count = 1;
	private boolean flag = false;
	
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	public void set(String name) throws InterruptedException{
		try{
			lock.lock();	//加锁
			while(flag == true){
				condition.await();	//替换wait()
			}
			this.name = name+"..."+count++;
			System.out.println(Thread.currentThread().getName()+"...生产者"+this.name);
			flag = true;
			condition.signalAll();	//替换notifyAll()
		}
		finally{
			lock.unlock();	//释放锁
		}
		
	}
	
	public void out() throws InterruptedException{
		try{
			lock.lock();	//加锁
			while(flag == false){
				condition.await();	//替换wait()
			}
			System.out.println(Thread.currentThread().getName()+"......消费者"+this.name);
			flag = false;
			condition.signalAll();	//替换notifyAll()
		}
		finally{
			lock.unlock();	//释放锁
		}
		
	}
}

class Producer implements Runnable{
	private Res r;
	public Producer(Res r) {
		this.r = r;
	}
	public void run() {
		while(true){
			try {
				r.set("+商品+");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

class Consumer implements Runnable{
	private Res r;
	public Consumer(Res r) {
		this.r = r;
	}
	public void run(){
		while(true){
			try {
				r.out();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

public class ProducerConsumerDemo{
	public static void main(String[] args) {
		Res r = new Res();
		Producer producer = new Producer(r);
		Consumer consumer = new Consumer(r);
		Thread t1 = new Thread(producer);
		Thread t2 = new Thread(producer);
		Thread t3 = new Thread(consumer);
		Thread t4 = new Thread(consumer);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

加入不用condition.signalAll()而使用condition.signal(),会出现全部线程等待现象,谁也动不了,类似于死锁,但不同于死锁,死锁是相互不让。


还有个新特性就是,一个Lock锁上可以有多个相关Condition,从而达到只唤醒对方线程的效果:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Res{
	private String name;
	private int count = 1;
	private boolean flag = false;
	
	private Lock lock = new ReentrantLock();
	private Condition condition_pro = lock.newCondition();
	private Condition condition_con = lock.newCondition();
	public void set(String name) throws InterruptedException{
		try{
			lock.lock();	//加锁
			while(flag == true){
				condition_pro.await();	//替换wait()
			}
			this.name = name+"..."+count++;
			System.out.println(Thread.currentThread().getName()+"...生产者"+this.name);
			flag = true;
			condition_con.signalAll();	//替换notifyAll()
		}
		finally{
			lock.unlock();	//释放锁
		}
		
	}
	
	public void out() throws InterruptedException{
		try{
			lock.lock();	//加锁
			while(flag == false){
				condition_con.await();	//替换wait()
			}
			System.out.println(Thread.currentThread().getName()+"......消费者"+this.name);
			flag = false;
			condition_pro.signalAll();	//替换notifyAll()
		}
		finally{
			lock.unlock();	//释放锁
		}
		
	}
}

class Producer implements Runnable{
	private Res r;
	public Producer(Res r) {
		this.r = r;
	}
	public void run() {
		while(true){
			try {
				r.set("+商品+");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

class Consumer implements Runnable{
	private Res r;
	public Consumer(Res r) {
		this.r = r;
	}
	public void run(){
		while(true){
			try {
				r.out();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

public class ProducerConsumerDemo{
	public static void main(String[] args) {
		Res r = new Res();
		Producer producer = new Producer(r);
		Consumer consumer = new Consumer(r);
		Thread t1 = new Thread(producer);
		Thread t2 = new Thread(producer);
		Thread t3 = new Thread(consumer);
		Thread t4 = new Thread(consumer);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

11.Thread线程类方法

前面讲了start(),wait(),notify(),notifyAll(),sleep()方法,下面讲解其他方法

a.中断线程

stop方法已经过时
 如何停止线程?
 只有一种,run方法结束
 开启多线程运行,运行代码通常是循环结构
 只要控制循环,就可以让run方法结束,也就是线程结束

 代码示例:
class StopThread implements Runnable{
	private boolean flag = true;
	public synchronized void run(){
		while(flag == true){
			System.out.println(Thread.currentThread().getName()+"...run");
		}
	}
	public void changeFlag(){
		flag = false;
	}
}

public class StopThreadDemo{
	public static void main(String[] args) {
		StopThread stopThread = new StopThread();
		Thread t1 = new Thread(stopThread);
		Thread t2 = new Thread(stopThread);
		t1.start();
		t2.start();
		
		int num = 0;
		while(true){
			if(num++ == 5){		
				stopThread.changeFlag();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"..."+num);
		}

	}
}


 特殊情况:
 当线程处于冻结状态就不会读取到标记,那么线程就不会结束
 
 当没有指定的方式让冻结的线程恢复到运行状态时,这时就需要对冻结进行清除
 强制将线程恢复到运行状态来,这样就可以操作标记让线程结束
 
 Thread类提供该方法 interrupt();


代码示例:
class StopThread implements Runnable{
	private boolean flag = true;
	public synchronized void run(){
		while(flag == true){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
				flag = false;	//interrupt后还是回去再等待,这句代码作用是让run方法真正结束
			}
			System.out.println(Thread.currentThread().getName()+"...run");
		}
	}
	public void changeFlag(){
		flag = false;
	}
}

public class StopThreadDemo{
	public static void main(String[] args) {
		StopThread stopThread = new StopThread();
		Thread t1 = new Thread(stopThread);
		Thread t2 = new Thread(stopThread);
		t1.start();
		t2.start();
		
		int num = 0;
		while(true){
			if(num++ == 5){		
				//stopThread.changeFlag();
				t1.interrupt();
				t2.interrupt();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"..."+num);
		}

	}
}


b.守护线程

当线程设置为守护线程时,其他线程运行结束,线程将自动结束,不用强制用interrupt结束。

方法为setDaemon(true);

代码示例:

class StopThread implements Runnable{
	private boolean flag = true;
	public synchronized void run(){
		while(flag == true){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
				flag = false;	//interrupt后还是回去再等待,这句代码作用是让run方法真正结束
			}
			System.out.println(Thread.currentThread().getName()+"...run");
		}
	}
	public void changeFlag(){
		flag = false;
	}
}

public class StopThreadDemo{
	public static void main(String[] args) {
		StopThread stopThread = new StopThread();
		Thread t1 = new Thread(stopThread);
		Thread t2 = new Thread(stopThread);
		t1.setDaemon(true);	//t1设置为守护线程
		t2.setDaemon(true);	//t2设置为守护线程
		t1.start();
		t2.start();
		
		int num = 0;
		while(true){
			if(num++ == 5){		
				//stopThread.changeFlag();
				//t1.interrupt();
				//t2.interrupt();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"..."+num);
		}

	}
}


b.抢夺线程

方法为join(),把主线程的cpu执行权抢过来。执行完线程再执行完其他线程。

代码示例:

class Demo implements Runnable{
	public void run(){
		for(int i=0;i<5;i++){
			System.out.println(Thread.currentThread().getName()+"..."+i);
		}
	}
}

public class JoinDemo{
	public static void main(String[] args)throws Exception{
		Demo d = new Demo();
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		t1.start();
		t1.join();
		t2.start();
		for(int i=0;i<5;i++){
			System.out.println(Thread.currentThread().getName()+"..."+i);
		}
		System.out.println("over");
	}
}


c.让步线程

方法为yield()

就是说当一个线程使用了这个方法之后,它就会把自己CPU执行的时间让掉,让自己或者其它的线程运行。相当于轮到自己了,却要和别的线程竞赛。

代码示例:

class Demo implements Runnable{
	public void run(){
		for(int i=0;i<5;i++){
			System.out.println(Thread.currentThread().getName()+"..."+i);
			Thread.yield();
		}
	}
}

public class JoinDemo{
	public static void main(String[] args)throws Exception{
		Demo d = new Demo();
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		t1.start();
		t2.start();
		
		for(int i=0;i<5;i++){
			System.out.println(Thread.currentThread().getName()+"..."+i);
		}
		System.out.println("over");
	}
}


d.优先级线程

方法为setPriority(Thread.MAX_PRIORITY);  代表最高优先级

setPriority(Thread.MIN_PRIORITY);  代表最低优先级

优先级只是概率上增加或者减少,而不是一定执行,最终还是由cpu决定

代码示例:

class Demo implements Runnable{
	public void run(){
		for(int i=0;i<5;i++){
			System.out.println(Thread.currentThread().getName()+"..."+i);
		}
	}
}

public class JoinDemo{
	public static void main(String[] args)throws Exception{
		Demo d = new Demo();
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		t1.start();
		t1.setPriority(Thread.MAX_PRIORITY);	//设置优先级最高
		t2.start();
		for(int i=0;i<5;i++){
			System.out.println(Thread.currentThread().getName()+"..."+i);
		}
		System.out.println("over");
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值