一、什么是进程,什么是线程
打开系统监视器,我们就可以查看正在运行的进程有哪些。例如:
以firefox为例,它是一个浏览器应用程序,当它开始运行的时候就成为一个进程(它为程序创建执行环境)。而firefox这个进程独自占有一些系统资源(例如CPU)和Memory(本例中当前状态是104.0KiB)。
Thread是进程中的单一的执行流,一个进程中可以存在多个线程,这些线程共享这个进程的拥有的系统资源。每个Java程序的main函数都是一条顺序执行流,下面是最简单的Java程序:
package org.vhow.java.thread;
public class AppMain
{
public static void main(String[] args)
{
}
}
我们来调试它:
可以看到,这个程序当前正在执行的Thread名为main。
二、为什么使用线程
1。 资源共享。
同一父进程的线程间共享这个进程所申请的系统资源。而不像进程那样各自占有一些系统资源。不过,线程可以拥有自己的栈空间、自己的程序计数器和自己的局部变量。
2。 并发执行。
线程是独立的执行流,每个线程都可以独立执行自己的顺序流,而不用等待其它线程。
三、线程的创建和启动
在我们使用迅雷下载文件的时候,可以同时下载多个文件。每个文件的下载都是一个顺序执行流,都是线程。这些线程并发执行。现在,我们用程序来模拟这个场景.
package org.vhow.java.thread;
public class AppMain
{
public static void main(String[] args)
{
DownLoader downloader = new DownLoader();
Thread movie = new Thread(downloader);
movie.setName("movie");
movie.start();
Thread music = new Thread(downloader);
music.setName("music");
music.start();
Thread text = new Thread(downloader);
text.setName("text file");
text.start();
System.out.println();
}
}
class DownLoader implements Runnable
{
@Override
public void run()
{
while (true)
{
System.out.println("downloading " + Thread.currentThread().getName());
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
在每个start()语句处设置断电,调试这个程序,我们会发现在执行打印空行的语句前,程序中同时存在四个线程:
除了像以上一样创建一个Runnable对象,我们还可以创建一个Thread类的扩展类来定义一个线程。例如:
package org.vhow.java.thread;
class DownThread extends Thread
{
@Override
public void run()
{
// do something in this thread
}
}
public class AppMain
{
public static void main(String[] args)
{
new DownThread().start();
}
}
四、线程同步
当多个线程同时访问同一个对象的数据时,可能会出现问题,例如下面的程序,ThreadA和ThreadB同时操作同一个值,一个对其加1,一个对其减1,可结果却不是原来的值:
package org.vhow.java.thread;
class Counter
{
private int value = 0;
public void increment()
{
int temp = this.getValue();
System.out.println(Thread.currentThread().getName()
+ " read the value is " + temp);
temp++;
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " increment the value by 1 ");
value = temp;
}
public void decrement()
{
int temp = this.getValue();
System.out.println(Thread.currentThread().getName()
+ " read the value is " + temp);
try
{
Thread.sleep(200);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " think the vlaue is " + temp + " but it is "
+ this.getValue() + " now.");
System.out.println(Thread.currentThread().getName()
+ " decrement the value by 1 ");
temp--;
value = temp;
}
public int getValue()
{
return value;
}
}
class ThreadA extends Thread
{
Counter counter = new Counter();
public ThreadA(Counter counter)
{
this.counter = counter;
}
@Override
public void run()
{
counter.increment();
}
}
class ThreadB extends Thread
{
Counter counter = new Counter();
public ThreadB(Counter counter)
{
this.counter = counter;
}
@Override
public void run()
{
counter.decrement();
}
}
public class AppMain
{
public static void main(String[] args) throws InterruptedException
{
Counter counter = new Counter();
ThreadA threadA = new ThreadA(counter);
threadA.setName("Thread-A");
threadA.start();
ThreadB threadB = new ThreadB(counter);
threadB.setName("Thread-B");
threadB.start();
threadA.join();
threadB.join();
System.out.println("The result is " + counter.getValue());
}
}
我们假设操作系统和程序本身都未添加多线程处理,这种情况就像是你和另一个人同时打开了服务器上的一个Excel文件并都做了一些修改,你先保存,那个人再并保存。这样,你修改的内容就消失了。那么我们怎么避免这样的事情发生呢。有两个办法:
1。 正和实际情况一下,如果已经有一个人打开了这个文档(正在编辑),在直到这个人完成编辑前,Excel就会阻止第二个人再次打开它。
2。 你先编辑,另一个人等待。在你完成编辑并保存后,通知另一个人。
对应的代码处理方法如下:
1.1 使用synchronized关键字同步方法。
public synchronized void increment()
{
value++;
}
public synchronized void decrement()
{
value--;
}
当已经有一个线程正在执行某个对象synchronized的方法时,其它想要执行同一个对象的synchronized的方法时,必须等待。
1.2 使用synchronized同步对象
public void increment()
{
synchronized (this)
{
value++;
}
}
public void decrement()
{
synchronized (this)
{
value--;
}
}
概念与上一个方法类似,当一个线程获得某个实例对象的锁并且没有释放之前,其它试图获得这个对象的锁的线程只能等待。
未完,待续。