对于多线程的理解
操作系统中有进程和线程两个相似的概念,其中
进程:每个进程都有独立的代码和数据空间,进程间切换会有较大的开销,进程是资源分配的最小单位
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器,线程切换开销小,线程是cpu调度的最小单位
五个阶段:创建(New),就绪(Runnable),运行(Running),阻塞(Blocked),终止(Dead)
多进程是操作系统能同时运行多个任务
多进程是同一程序中有多个顺序流在执行
java实现多线程可通过继承Thread类,或者重写Runnable接口,或者实现Callable接口并与Future、线程池来实现。如果继承Thread则不适合资源共享,如果实现了Runable接口能很容易实现资源共享(适合多个线程去处理同一个资源,避免单继承的限制)
java的进程同步通过synchronized()实现,他通过对内存加锁使其他线程无法访问该内存实现简单的同步,互斥操作
而通过Object.wait(), Object.nofity()则可以实现线程间相互唤醒。
例如,连续交替打印十次ABC。
package ceshi;
public class Main {
static class My implements Runnable
{
private String name;
private Object prev;
private Object self;
private My(String name, Object prev, Object self)
{
this.name = name;
this.prev = prev;
this.self = self;
}
public void run()
{
int count = 10;
while(count>0)
{
synchronized(prev)
{
synchronized(self)
{
System.out.print(name);
count--;
self.notify();//释放自身对象锁
}
try
{
prev.wait();//释放prev对象锁
}catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
public static void main(String[]args)throws Exception
{
Object a = new Object();
Object b = new Object();
Object c = new Object();
Object d = new Object();
My pa = new My("A", d, a);
My pb = new My("B", a, b);
My pc = new My("C", b, c);
My pd = new My("D", c, d);
new Thread(pa).start();
System.out.println("aaaaaa");
new Thread(pb).start();
System.out.println("bbbbbb");
new Thread(pc).start();
System.out.println("cccccc");
new Thread(pd).start();
System.out.println("dddddd");
}
}
输出为
可见程序的运行顺序是依次打开pa,pb,pc,pd然后循环进行
修改一下,核心代码变成这样以后
while(count>0)
{
synchronized(prev)
{
synchronized(self)
{
System.out.print(name);
count--;
try
{
Thread.sleep(1);
}catch(InterruptedException e)
{
e.printStackTrace();
}
self.notify();
}
try
{
prev.wait();
}catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
输出仍然不正确,结果为
这是由于JVM调度的不确定性,需要让ABCD按照确定的顺序启动
new Thread(pa).start();
Thread.sleep(10);
new Thread(pb).start();
Thread.sleep(10);
new Thread(pc).start();
Thread.sleep(10);
new Thread(pd).start();
Thread.sleep(10);
每次启动一个线程以后,令其休眠,且休眠时间长于释放自身对象锁和prev对象锁的时间,则能够保证程序按照要求的顺序进行。
start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的,Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。多线程的代码执行顺序是不确定的,执行的结果也是随机的。
线程状态转换
先new Thread,然后start使线程变成可运行态,先抢到cpu资源的线程开始运行,synchronized的线程对内存加锁获得独占访问,然后sleep.wait或者被其他线程占用的线程将进入阻塞状态,等到重新到达可运行态才有机会被运行
java线程具有优先级,用setPriority()和getPriority()方法分别用来设置和获取线程的优先级,其中分配给线程的默认优先级为5,最低为1,最高为10;
常用函数:
1.线程睡眠 Thread.sleep(long millis)方法使线程转到阻塞状态,设定的时间结束后转为Runnable,sleep平台移植性好
2.线程等待:Object的wait方法导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。
3.线程让步:Thread.yield方法暂停当前线程,把执行机会让给相同或优先级更高的线程
4.线程加入:join方法,在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
5.线程唤醒:Object中的notify方法,唤醒等待的单个线程
Future
Future是一个未来对象,里面保存这线程处理结果,通过实现Callback接口,并用Future可以来接收多线程的执行结果。 Future表示一个可能还没有完成的异步任务的结果,针对这个结果可以添加Callback以便在任务执行成功或失败后作出相应的操作。
它可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
cancel方法用来取消任务
isCancelled方法表示任务是否被取消成功
isDone方法表示任务是否已经完成
get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null
举个栗子:买包子(3秒)和凉菜(1秒)
普通多线程需要4秒
package ceshi;
public class Main {
static class Bao extends Thread
{
public void run()
{
try
{
Thread.sleep(1000*3);
System.out.println("包子做好了");
}catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
static class Liang extends Thread
{
public void run()
{
try
{
Thread.sleep(1000);
System.out.println("凉菜做好了");
}catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
public static void main(String[]args)throws InterruptedException
{
long start = System.currentTimeMillis();
Thread t1 = new Liang();
t1.start();
t1.join();
Thread t2 = new Bao();
t2.start();
t2.join();
long end = System.currentTimeMillis();
System.out.println("总时间: "+(end-start)/1000+"秒");
}
}
结果是4秒
事实上只需要三秒
package ceshi;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Main {
public static void main(String[]args)throws Exception
{
long start = System.currentTimeMillis();
Callable ca1 = new Callable() {
public String call()throws Exception{
try {
Thread.sleep(1000);
}catch(InterruptedException e)
{
e.printStackTrace();
}
return "凉菜做好了";
}
};
FutureTask<String>ft1 = new FutureTask<String >(ca1);
new Thread(ft1).start();
Callable ca2 = new Callable()
{
public String call()throws Exception{
try {
Thread.sleep(1000*3);
}catch(InterruptedException e)
{
e.printStackTrace();
}
return"包子做好了";
}
};
FutureTask<String>ft2 = new FutureTask<String>(ca2);
new Thread(ft2).start();
System.out.println(ft1.get());
System.out.println(ft2.get());
long end = System.currentTimeMillis();
System.out.println("总时间: "+(end - start)/1000+"秒");
}
}