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
将若干功能相似的线程放到一个组中,便于统一管理。如统一设置优先级等