Java多线程基础

    Java编写的程序都运行在 Java 虚拟机 (JVM) 中,在 JVM 的内部,程序的多任务是通过多线程来实现的。每用 java 命令启动一个 java 应用程序,就会启动一个 JVM 进程。在同一个 JVM 进程中,有且只有一个进程,就是它自己。在这个 JVM 环境中,所有程序代码的运行都是以线程来运行。

    对于一个进程中的多个线程来说,多个线程共享进程的内存块,当有新的线程产生的时候,操作系统不分配新的内存,而是让新线程共享原有的进程块的内存。因此,线程间的通信 很容易,速度也很快。不同的进程因为处于不同的内存块,因此进程之间的通信相对困难。

 

实现多线程的两种方式

    一种是继承 Thread类,一种是实现 Runnable 接口。使用线程的时候主要注意下面两点,一是如何指定线程需要执行的方法,二是如何启动一个线程。新线程的创建跟启动都是通过java代码来触发的,是由java代码通知java平台,java平台再启动线程,启动新线程是一个很短的过程,对于主线程来说,只是通知java平台要启动新线程,通知完了就继续执行代码,不需要等待新线程结果,只有所有线程都退出了,程序才退出。 Thread是定义中 java.lang 包中的,一个类只要继承了 Thread 类并且重写了 run 方法,就可以实现多线程操作,如 下定义的MyThread 类,继承 Thread 类并重写 run 方法:

public class MyThread extends Thread {	
	private String	name;	
	public MyThread(String name) {		
		this.name = name;		
	}	
	public void run() {		
		for (int i = 0; i < 10; i++) {			
			System.out.println("name:" + name + ",i=" + i);			
		}		
	}	
}
 

  操作类 MyThreadTest:

public class MyThreadTest{	
	public static void main(String[] args) {		
		MyThread thread1 = new MyThread("线程A");		
		MyThread thread2 = new MyThread("线程B");		
		thread1.run();
		thread2.run();		
	}	
}
 

运行结果如下:

name:线程A,i=0

name:线程A,i=1

name:线程A,i=2

name:线程A,i=3

name:线程A,i=4

name:线程A,i=5

name:线程A,i=6

name:线程A,i=7

name:线程A,i=8

name:线程A,i=9

name:线程B,i=0

name:线程B,i=1

name:线程B,i=2

name:线程B,i=3

name:线程B,i=4

name:线程B,i=5

name:线程B,i=6

name:线程B,i=7

name:线程B,i=8

name:线程B,i=9

 

    发现程序的运行是先运行线程 A完成后再运行线程 B ,不是交替运行,说明程序不是按多线程方式运行,而是按单线程方式运行,正确的启动多线程操作方法应该是调用 start() 方法, JVM 会自动调用该对象的 run() 方法,如上把线程操作类My ThreadTest 类的 run ()方法改为 start ()方法后,执行结果如下:

name:线程A,i=0

name:线程B,i=0

name:线程A,i=1

name:线程B,i=1

name:线程A,i=2

name:线程B,i=2

name:线程A,i=3

name:线程B,i=3

name:线程A,i=4

name:线程B,i=4

name:线程A,i=5

name:线程B,i=5

name:线程A,i=6

name:线程B,i=6

name:线程A,i=7

name:线程B,i=7

name:线程A,i=8

name:线程B,i=8

name:线程A,i=9

name:线程B,i=9

此时已经可以实现多线程调用,但是使用继承Thread 类实现多线程有个限制, java 语言只能继承一个类,而不能多继承,此时可以使用第二种方式实现,就是实现 Runnable 接口。 开发中很少使用继承Thread 类实现多线程操作的,一般都使用实现 Runnable 接口,如下实现多线程类 MyRunnable

public class MyRunnable implements Runnable {  
    private String name;      
    public MyRunnable(String name) {  
        this.name = name;  
    }     
    public void run() {  
        for (int i = 0; i < 10; i++) {  
            System.out.println("name:" + name + ",i=" + i);  
        }  
    }  
} 
 

  多线程操作类:

public class MyRunnableTest{  
    public static void main(String[] args) {  
        MyRunnable thread1 = new MyRunnable("线程A");  
        MyRunnable thread2 = new MyRunnable("线程B");  
        new Thread(thread1).start();  
        new Thread(thread2).start();  
    }  
} 
 

    实现Runnable 接口实现的多线程类并没有 start() 方法,而是使用 Thread 类的代理来实现多线程的操作。Thread类中有一个类型为Runnable的属性为target,Thread类中的run()方法就是调用该 Runnable 属性的run()方法,如下:

    private Runnable target;    
    ......
    public void run() {
	if (target != null) {
	    target.run();
	}
    }
 

继承Thread 类实现的多线程不能实现资源共享,而实现 Runnable 接口实现的多线程可以实现资源共享。 如下使用继承Thread 实现的多线程类 MyThread ,模拟卖票场景操作类变量 ticket

public class MyThread extends Thread {	
	private int	ticket	= 5;	
	public void run() {		
		for (int i = 0; i < 50; i++) {			
			if (ticket > 0) {				
				System.out.println("卖票:tichek=" + ticket--);				
			}			
		}		
	}	
}
   多线程操作类为:

 

public class ThreadTicket {	
	public static void main(String[] args) {		
		MyThread thread1 = new MyThread();		
		MyThread thread2 = new MyThread();		
		MyThread thread3 = new MyThread();		
		thread1.start();		
		thread2.start();		
		thread3.start();		
	}	
}
程序输出为:

卖票:tichek=5

卖票:tichek=4

卖票:tichek=3

卖票:tichek=5

卖票:tichek=4

卖票:tichek=5

卖票:tichek=4

卖票:tichek=3

卖票:tichek=2

卖票:tichek=1

卖票:tichek=3

卖票:tichek=2

卖票:tichek=1

卖票:tichek=2

卖票:tichek=1

发现每个线程都操作ticket 变量 5 次,而不是操作一个共享变量,因为这里实例化了3个MyThread对象,把 MyThread 的多线程实现改为实现 Runnable 接口,如:

public class ThreadTicket {	
	public static void main(String[] args) {		
		MyRunnable thread1 = new MyRunnable();		
		new Thread(thread1).start();		
		new Thread(thread1).start();		
		new Thread(thread1).start();		
	}	
}

输出为:

卖票:tichek=5
卖票:tichek=3
卖票:tichek=2
卖票:tichek=1
卖票:tichek=4
说明 3 个线程操作的是一个变量,因为这里只实例化了一个MyRunnable对象。

 

 

多线程中所有操作方法都是在Thread 类中定义的,如:

设置设置线程名称方法:

1、 使用set 方法: public final void setName(String name)

2、 构造方法:public Thread(Runnable target,String name),

             public Thread(String name);

取得名字:public final String getName();

对于线程的名字,最好是在线程启动前进行设置,最好不要设置相同名字,也不要为一个线程改名字。

Thread类还提供一个静态方法来获取当前操作的线程: public static Thread currentThread();

如下线程实现类:

public class MyRunnable implements Runnable {	
	public void run() {
		for (int i = 0; i < 10; i++) {			
			System.out.println(Thread.currentThread().getName() + " 正在运行...");
		}		
	}	
}
 

  线程操作类:

public class ThreadNameDemo {	
	public static void main(String[] args) {		
		MyRunnable myrunnable = new MyRunnable();		
		Thread thread1 = new Thread(myrunnable, "线程A");		
		Thread thread2 = new Thread(myrunnable, "线程B");		
		Thread thread3 = new Thread(myrunnable, "线程C");		
		thread1.start();		
		thread2.start();		
		thread3.start();		
	}	
}
 

运行输出:

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程B 正在运行...

线程B 正在运行...

线程B 正在运行...

线程B 正在运行...

线程B 正在运行...

线程B 正在运行...

线程B 正在运行...

线程B 正在运行...

线程B 正在运行...

线程B 正在运行...

线程C 正在运行...

线程C 正在运行...

线程C 正在运行...

线程C 正在运行...

线程C 正在运行...

线程C 正在运行...

线程C 正在运行...

线程C 正在运行...

线程C 正在运行...

线程C 正在运行...

把线程实现类改成:

public class ThreadNameDemo {	
	public static void main(String[] args) {		
		MyRunnable myrunnable = new MyRunnable();		
		Thread thread1 = new Thread(myrunnable, "线程A");		
		thread1.start();		
		myrunnable.run();		
	}	
}
 

运行输出:

main 正在运行...

main 正在运行...

main 正在运行...

main 正在运行...

main 正在运行...

main 正在运行...

main 正在运行...

main 正在运行...

main 正在运行...

main 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

线程A 正在运行...

说明在程序运行时,主方法就是一个主 mail线程。

 

Thread类的 sleep() 方法实现线程的休眠,如下线程实现类:


public class MyRunnable implements Runnable {	
	public void run() {		
		for (int i = 0; i < 5; i++) {			
			try {				
				Thread.sleep(300);				
			}
			catch (InterruptedException e) {				
				// TODO : handle exception				
			}			
			System.out.println(Thread.currentThread().getName() + " 正在运行...");			
		}		
	}	
}
 

线程操作类为:

public class ThreadSleepDemo {	
	public static void main(String[] arge) {
		MyRunnable myrunnable = new MyRunnable();		
		new Thread(myrunnable, "线程A").start();		
		new Thread(myrunnable, "线程B").start();		
	}	
}
 

运行结果为:

线程B 正在运行...

线程A 正在运行...

线程B 正在运行...

线程A 正在运行...

线程B 正在运行...

线程A 正在运行...

线程B 正在运行...

线程A 正在运行...

线程B 正在运行...

线程A 正在运行...

 

Thread类提供 interrupt() 类提供中断线程的执行。

 

Thread类提供 public   final   void   setPriority ( int  newPriority)  方法设置线程优先级,线程优先级有下面三种:

最高: MAX_PRIORITY     10

中等: NORM_PRIORITY    5

最低: MIN_PRIORITY     1

Main线程的优先级为 5

 

线程同步

使用上面模拟卖票程序,如果卖票程序中加入延迟,则运行的效果跟之前的不一样,如:

public class MyRunnable implements Runnable {	
	private int	ticket	= 5;	
	public void run() {		
		for (int i = 0; i < 50; i++) {			
			if (ticket > 0) {				
				try {					
					Thread.sleep(300);					
				}
				catch (InterruptedException e) {					
					// TODO Auto-generated catch block					
					e.printStackTrace();					
				}				
				System.out.println("卖票:tichek=" + ticket--);				
			}			
		}		
	}	
}
 

  线程操作类不变:

public class ThreadTicket {	
	public static void main(String[] args) {		
		MyRunnable myrunnable = new MyRunnable();		
		new Thread(myrunnable).start();		
		new Thread(myrunnable).start();
		new Thread(myrunnable).start();		
	}	
}
 

运行结果为:

卖票:tichek=5

卖票:tichek=3

卖票:tichek=4

卖票:tichek=2

卖票:tichek=1

卖票:tichek=0

卖票:tichek=-1

 

java中可以通过同步代码进行代码的加锁操作,有两种实现方法:

1、 同步代码块

使用synchronized 关键字进行同步代码块声明,但是在此操作时必须要明确指出到底锁定的是哪个对象,一般都是以当前对象 this 为主,如上面模拟卖票代码,加上同步后为:

public class MyRunnable implements Runnable {	
	private int	ticket	= 5;	
	public void run() {		
		for (int i = 0; i < 50; i++) {			
			synchronized (this) {				
				if (ticket > 0) {					
					try {						
						Thread.sleep(300);						
					}
					catch (InterruptedException e) {						
						// TODO Auto-generated catch block						
						e.printStackTrace();						
					}					
					System.out.println("卖票:tichek=" + ticket--);					
				}				
			}			
		}		
	}	
}
  此时输出正常,但是输出速度明显变慢。

 

2、 同步方法

模拟上面卖票程序,改用同步方法实现为:

public class MyRunnable implements Runnable {	
	private int	ticket	= 5;	
	public void run() {		
		for (int i = 0; i < 50; i++) {			
			sale();			
		}		
	}	
	public synchronized void sale() {		
		if (ticket > 0) {			
			try {				
				Thread.sleep(300);				
			}
			catch (InterruptedException e) {				
				// TODO Auto-generated catch block				
				e.printStackTrace();				
			}			
			System.out.println("卖票:tichek=" + ticket--);			
		}
		
	}
	
}
 

在程序中过多的同步为出现死锁,关于Java中锁机制和synchronized关键字看下面两篇文章:

http://chenzehe.iteye.com/blog/1741046

http://chenzehe.iteye.com/blog/1766790

 

 

生产者和浪费者,生产者不断生产内容,消费者不断取出内容,如下基础类Info ,生产者和消费者不断对其进行操作:


public class Info {	
	private String	country;	
	private String	city;	
	public String getCountry() {		
		return country;		
	}	
	public void setCountry(String country) {		
		this.country = country;		
	}	
	public String getCity() {		
		return city;		
	}	
	public void setCity(String city) {		
		this.city = city;		
	}	
}

//生产者,不断的对Info 对象设值:
public class Product implements Runnable {	
	private Info	info	= null;	
	public Product(Info info) {		
		this.info = info;		
	}	
	public void run() {		
		for (int i = 0; i < 10; i++) {			
			if (i % 2 == 1) {				
				info.setCountry("中国");				
				try {				
					Thread.sleep(300);					
				}
				catch (InterruptedException e) {					
					// TODO Auto-generated catch block					
					e.printStackTrace();					
				}				
				info.setCity("杭州");				
			}
			else {				
				info.setCountry("美国");				
				try{
                                        Thread.sleep(300);					
				}
				catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				info.setCity("硅谷");
			}			
		}		
	}	
}

//消费者,不断的对 Info对象取值 :
public class Customer implements Runnable {	
	private Info	info	= null;	
	public Customer(Info info) {		
		this.info = info;		
	}	
	public void run() {		
		for (int i = 0; i < 10; i++) {			
			try {				
				Thread.sleep(300);				
			}
			catch (InterruptedException e) {				
				// TODO Auto-generated catch block				
				e.printStackTrace();				
			}			
			System.out.println("country:" + info.getCountry() + "->city:"
			+ info.getCity());			
		}		
	}	
}

//多线程操作类:
public class ProCusDemo {	
	public static void main(String[] args) {		
		Info info = new Info();		
		Product product = new Product(info);		
		Customer customer = new Customer(info);		
		new Thread(product).start();		
		new Thread(customer).start();		
	}	
}
 

运行输出为:

country:中国->city:硅谷

country:美国->city:杭州

country:中国->city:硅谷

country:美国->city:杭州

country:中国->city:硅谷

country:美国->city:杭州

country:中国->city:硅谷

country:美国->city:杭州

country:中国->city:硅谷

country:中国->city:杭州

上面代码出现了生产的内容有可能不一至的情况,还出现了重复取值和重复设值的情况,为了保证数据的完整性,一般加入同步操作,如 Info类加入同步方法后为:

public class Info {
	private String country;
	private String city;
	public synchronized void set(String country, String city) {
		this.country = country;
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.city = city;
	}
	public synchronized void get() {
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("country:" + this.country + "->city:" + this.city);
	}
	public String getCountry() {
		return country;
	}
	public void setCountry(String country) {
		this.country = country;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
}
生产者为:
public class Product implements Runnable {
	private Info info = null;
	public Product(Info info) {
		this.info = info;
	}
	public void run() {
		for (int i = 0; i < 10; i++) {
			if (i % 2 == 1) {
				info.set("中国", "杭州");
			} else {
				info.set("美国", "硅谷");
			}
		}
	}
}
消费者为:
public class Customer implements Runnable {
	private Info info = null;
	public Customer(Info info) {
		this.info = info;
	}
	public void run() {
		for (int i = 0; i < 10; i++) {
			info.get();
		}
	}
}
多线程操作类不变,输出为:

 

country:美国->city:硅谷

country:中国->city:杭州

country:中国->city:杭州

country:中国->city:杭州

country:中国->city:杭州

country:中国->city:杭州

country:中国->city:杭州

country:中国->city:杭州

country:美国->city:硅谷

country:中国->city:杭州

此时能够保证代码的一至性,但还是存在重复取和重复设置问题,可以使用Object 类的 wait() 方法和 notiry() 方法进行修改,如: Info类为:

public class Info {
	private String country;
	private String city;
	private boolean flag = true; // true表示可以生产,但不能消费,false表示可以消费,但不能生产
	public synchronized void set(String country, String city) {
		if (!flag) {
			try {
				super.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.country = country;
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.city = city;
		this.flag = false;
		super.notify();
	}
	public synchronized void get() {
		if (flag) {
			try {
				super.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("country:" + this.country + "->city:" + this.city);
		this.flag=true; 
		super.notify();
	}
	public String getCountry() {
		return country;
	}
	public void setCountry(String country) {
		this.country = country;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
}
 

其它类不变,运行结果为:

country:美国->city:硅谷

country:中国->city:杭州

country:美国->city:硅谷

country:中国->city:杭州

country:美国->city:硅谷

country:中国->city:杭州

country:美国->city:硅谷

country:中国->city:杭州

country:美国->city:硅谷

country:中国->city:杭州

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值