`

多线程基础

    博客分类:
  • Java
 
阅读更多

http://www.ibm.com/developerworks/library/j-jtp0730/index.html

 

 

 

学习编程的目的:解决问题/开发用户需要的功能

 

多线程可以解决什么问题?

将不同的子任务交给不同的线程去执行,提高系统响应速度(比单一线程执行所有任务更快)

 

从最简单的开始,如何创建线程!

run() : 封装线程要运行的任务,所以将线程要运行的代码封装到run()中即可!

方式一:创建一个类直接继承Thread类,并覆盖run()

方式二:创建一个类实现Runnable接口,实现run(),再将此类的实例传入Thread构造方法中

 

开启线程:

new Thread().start(); 

new Thread(MyTask).start();   [ MyTask implements Runnable ]

 

 

 

线程的几种状态切换



 

 

 两种使用线程的方式:

 

package thread;

/**
 * 多线程使用方式一:
 * 	通过继承Thread类让子类成为线程类
 * 	再通过该线程类开启线程执行任务
 *
 */
public class ThreadDemo_A extends Thread {
	
	public ThreadDemo_A(String threadName) {
		//将线程名称传入父类构造方法,设置父线程的name,子类再将此name继承下来
		super(threadName);
	}

	@Override
	public void run() {//run()封装线程需要执行的任务
		for(int i=0;i<100;i++) {
			//获取当前线程名称
			System.out.println(Thread.currentThread().getName());
		}
	}
	
	
	
	public static void main(String[] args) {
                //主线程
		System.out.println("main() start");
		
		ThreadDemo_A a = new ThreadDemo_A("work_11111");
		a.start();//开启另外1个线程
		
		new ThreadDemo_A("work_22222").start();//再开启1个线程
		
		System.out.println("main() end");
	}
}

 

推荐的方式--->通过Runnable封装线程任务

package thread;

/**
 * 多线程使用方式二:
 * 	通过实现 Runnable 接口,在run()中定义需要由线程执行的任务
 * 	然后将子类实例传递到Thread构造函数中,让Thread类去开启线程执行子类中的run()
 *
 */
public class ThreadDemo_B implements Runnable {

	private String address;
	
	private String content;
	
	public ThreadDemo_B() {}
	
	public ThreadDemo_B(String address, String content) {
		this.address = address;
		this.content = content;
	}
	
	@Override
	public void run() {
		sendEmail();//线程任务:发送邮件
	}

	private void sendEmail() {
		for(int i=0;i<100;i++)
			System.out.println(Thread.currentThread().getName()+"------->>>>"+"Send email to " + address +", content=" + content);
	}
	
	
	public static void main(String[] args) {
		ThreadDemo_B task1 = new ThreadDemo_B("1@1.com", "1111111");
		ThreadDemo_B task2 = new ThreadDemo_B("2@2.com", "2222222");
		new Thread(task1,"线程111111").start();
		new Thread(task2,"线程222222").start();
	}
}

 

 

 

 

==========================================================================

 

单个线程操作资源不存在同步、异步。

 

同步:WC,进入后上锁,出来后打开锁。

通过同步(加锁)来解决多线程操作共享资源时的安全问题!

同步的弊端:效率低(操作共享资源之前,线程都需要判断锁,如果锁没有释放,是进入不了的,会一直等待锁的释放)。

 

什么时候需要使用同步?

多个线程操作共享资源

且必须使用同一个锁,才能实现多线程同步访问的效果!

 

不同情况下的同一 个锁:

同步代码块使用的锁:堆内存中的任何相同对象(内存地址相同,即同一个对象)。

Runnable中定义一个成员对象,如 Object locker = new Object();

synchronized(locker)使用的就是同一个锁;

同步函数使用的锁:this

 静态同步函数使用的锁:该方法所属类的字节码对象 Ticket.class

 

Java提供的处理同步问题的办法:

同步代码块

public void doIt() {
	doSomething....
	
	//操作共享资源的代码,使用同步代码库对其封装
	synchronized (Task.class) {
		...
	}
	doOtherthing...
}

 

 

同步函数

//使用同步函数封装那些涉及共享资源操作的代码
public synchronized void doIt() {
 //操作共享资源的代码
 code...
}

 

由于同步中使用的锁可以是任何对象,所以wait(),notify(),notifyAll()被定义在Object中!

 

wait(),notify(),notifyAll()是绑定在同一个锁上相互进行通信的!

 

wait() : 某种不满足操作的情况下,线程让自己进入等待状态!locker.wait()

 

notify(): 当线程自己进入等待状态后,调用notify()唤醒线程池中的一个线程,只唤醒一个(可能唤醒的是本方线程,造成死锁)。locker.notify()

 

notifyAll() : 唤醒线程池中所有等待的线程,可以避免死锁的发生。locker.notifyAll()

 

 

package thread;

/**
 * 简单的2个线程间交替执行示例
 * 通过synchronized保证对资源的“原子操作”不被打断 
 * 通过线程通信实现切换运行
 */
public class TwoThreadsCommunication {
	
	
	public static void main(String[] args) {
		
		new TwoThreadsCommunication().justDoIt();
		
	}
	
	public void justDoIt() {
		
		final ResourceHandler r = new ResourceHandler();
		
		
		new Thread(new Runnable() {
			public void run() {
				for(int i=0;i<100;i++)
					r.produce();
			}
		}, "线程A").start();
		
		new Thread(new Runnable() {
			public void run() {
				for(int i=0;i<100;i++)
					r.consume();
			}
		}, "线程B").start();
	}
	
	
	/**
	 * 资源类
	 * 
	 * 将多线程操作的代码单独封装起来,然后在run()中通过对象来调用
	 * 将同步放到资源上,而不是在run()中进行控制,实现与具体线程的解耦
	 * 互斥不要放到线程上进行,而应该放到对资源类操作的方法中!!!
	 */
	class ResourceHandler {
		
		//状态变量在资源内部进行操作
		private boolean full;
		
		//生产线程操作共享资源的方法
		public synchronized void produce() {
			//notice: 这里用while,不要用if。可防止死锁!
			while(full) {
				try {
					this.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			for(int i=1;i<=10;i++) {
				System.out.println(Thread.currentThread().getName()+" run***" + i);
			}
			full = true;
			this.notify();
		}
		
		//消费线程操作共享资源的方法
		public synchronized void consume() {
			//notice: 这里用while,不要用if。可防止死锁!
			while(!full) {
				try {
					this.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			for(int j=1;j<=10;j++) {
				System.out.println(Thread.currentThread().getName()+" run******" + j);
			}
			full = false;
			this.notify();
		}
		
	}
}

 

 

多线程间共享资源的方式:

package thread;
/**
 * 多线程共享资源的方式一:
 * 
 * 操作共享资源的run()放到内部类中,然后操作外部类中定义的资源
 * 
 * 1个线程负责加,1个线程负责减
 */
public class ShareDataStyle1 {
	
	private int sharedData = 100;
	
	public synchronized void add() {
		sharedData++;
		System.out.println(Thread.currentThread().getName()+" add:" + sharedData);
	}
	
	public synchronized void minus() {
		sharedData--;
		System.out.println(Thread.currentThread().getName()+" minus:" + sharedData);
	}
	
	//内部类访问外部类的共享资源
	class Add implements Runnable {
		public void run() {
			while(true) 
				add();
		}
	}
	
	//内部类访问外部类的共享资源
	class Minus implements Runnable {
		public void run() {
			while(true) 
				minus();
		}
	}
	
	public static void main(String[] args) {
		ShareDataStyle1 sharedDate = new ShareDataStyle1();
		new Thread(sharedDate.new Add()).start();
		new Thread(sharedDate.new Minus()).start();
	}
	
}

 

package thread;


/**
 * 多线程共享资源的方式二:
 * 
 * 将共享资源传递到不同的Runnable对象中
 * 
 * 1个线程负责加,1个线程负责减
 */
public class ShareDataStyle2 {
	
	public static void main(String[] args) {
		SharedData data = new SharedData();
		new Thread(new Add(data)).start();
		new Thread(new Minus(data)).start();
	}
	
}


class SharedData {
	private int sharedData = 100;
	
	public synchronized void add() {
		sharedData++;
		System.out.println(Thread.currentThread().getName()+" add:" + sharedData);
	}
	
	public synchronized void minus() {
		sharedData--;
		System.out.println(Thread.currentThread().getName()+" minus:" + sharedData);
	}
}

class Add implements Runnable {
	SharedData sharedData;
	public Add(SharedData sharedData) {
		this.sharedData = sharedData;
	}
	
	public void run() {
		while(true) 
			sharedData.add();
	}
}

class Minus implements Runnable {
	SharedData sharedData;
	public Minus(SharedData sharedData) {
		this.sharedData = sharedData;
	}
	
	public void run() {
		while(true) 
			sharedData.minus();
	}
}

 

 

 

 

异步:游泳池,往里跳,往外出,互不干扰,这就是异步。

 

同步:解决多个线程操作共享资源发生安全隐患!

A线程在执行某个代码片段,B线程在A线程释放锁之前将被阻塞(同一个监视器下)。

 

而异步呢:多个线程以并行工作的方式协同完成整体任务,目的是更快的完成当前任务!

一个耗时较长的任务,如果可以被分割为几个小的部分,而且这几个部分没有前后依赖关系,则可以并行的执行(充分利用多CPU的能力),提高处理速度。

 

=======================================================================

 

 

几个容易混淆的概念

 

wait : 当前线程将释放CPU,释放锁


sleep : 当前线程将释放CPU,不释放锁

 

InterruptedException:

线程当前所处状态被清除时,如,本来线程A正在sleep(5000),当到2000ms时,突然其sleep状态被清除了,此时,线程A将继续进行执行。只是,这种情况发生之后,JVM会抛出InterruptedException 到该线程上,告诉你线程A非正常醒了,是否需要处理这种非正常的醒就看程序员如果处理该异常了!

需要理解的是:发生InterruptedException不代表当前线程结束了

一般情况下,这种非正常状态改变发生后,直接让方法return即可!

调用线程t的interrupt()便可以让t线程发生InterruptedException,然后在catch块中改变程序执行控制标记,或者return都行,具体怎么弄就看情况了!


守护线程:setDeamon(boolean b)

当所有线程都是守护线程时,JVM就会自动退出。

如,设置一个守护线程专门管理系统的缓存:

Thread cacheThread = new Thread(new CacheManager());

cacheThread.setDeamon(true); //必须在开启线程之前进行设置

cacheThread.start();

 

线程结合点:join() 

切入一个新的线程到当前环境,只有切入的线程执行完毕后,当前线程才会继续执行

Thread subThread = new SubThread();

subThread.join();//直到subThread执行完毕,当前线程才继续往下执行

 

线程优先级

Thread t1 = new Thread();
 t1.setPriority(Thread.MAX_PRIORITY);//最高优先级:10


 Thread t2 = new Thread();
 t2.setPriority(Thread.MIN_PRIORITY);//最低优先级:1


 Thread t3 = new Thread();
 t3.setPriority(Thread.NORM_PRIORITY);//默认优先级:5 

 

线程组ThreadGroup

 将若干功能相似的线程放到一个组中,便于统一管理。如统一设置优先级等

  • 大小: 60.3 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics