进程是程序在处理机中的一次运行。一个进程既包括其所要执行的指令,也包括了执行指令所需的系统资源,不同进程所占用的系统资源相对独立。所以进程是重量级的任务,它们之间的通信和转换都需要操作系统付出较大的开销。
线程是进程中的一个实体,是被系统独立调度和分派的基本单位。线程自己基本上不拥有系统资源,但它可以与同属一个进程的其他线程共享进程所拥有的全部资源。所以线程是轻量级的任务,它们之间的通信和转换只需要较小的系统开销。
Java支持多线程编程,因此用Java编写的应用程序可以同时执行多个任务。Java的多线程机制使用起来非常方便,用户只需关注程序细节的实现,而不用担心后台的多任务系统。
Java语言里,线程表现为线程类。Thread线程类封装了所有需要的线程操作控制。在设计程序时,必须很清晰地区分开线程对象和运行线程,可以将线程对象看作是运行线程的控制面板。在线程对象里有很多方法来控制一个线程是否运行,睡眠,挂起或停止。线程类是控制线程行为的唯一的手段。一旦一个Java程序启动后,就已经有一个线程在运行。可通过调用Thread.currentThread方法来查看当前运行的是哪一个线程。
JAVA中创建线程可以通过继承Thread类和实现Runnable接口来创建一个线程。Runnable方式可以避免Thread 方式由于JAVA单继承特性带来的缺陷。Runnable的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况。
方式一:继承Thread
class MyThread extends Thread{
private int ticketsCont=5; //一共有5张火车票
private String name; //窗口, 也即是线程的名字
public MyThread(String name){
this.name=name;
}
@Override
public void run(){
while(ticketsCont>0){
ticketsCont--; //如果还有票,就卖掉一张票
System.out.println(name+"卖掉了1张票,剩余票数为:"+ticketsCont);
}
}
}
public class TicketsThread
{
public static void main(String args[]){
//创建三个线程,模拟三个窗口卖票
MyThread mt1=new MyThread("窗口1");
MyThread mt2=new MyThread("窗口2");
MyThread mt3=new MyThread("窗口3");
//启动三个线程,也即是窗口,开始卖票
mt1.start();
mt2.start();
mt3.start();
}
}
方式二:实现Runnable接口
class MyThread2 implements Runnable
{
private int ticketsCont=1000; //一共有5张火车票
@Override
public void run(){
while(true){
synchronized(this){
if(ticketsCont<=0){
break;
}
ticketsCont--; //如果还有票,就卖掉一张票
System.out.println(Thread.currentThread().getName()+"卖掉了1张票,剩余票数为:"+ticketsCont);
/*try{
Thread.sleep(50); //通过阻塞程序来查看效果
}
catch(Exception e){
System.out.println(e);
}*/
}
}
}
/*@Override //不加同步锁
public void run(){
while(ticketsCont>0){
ticketsCont--; //如果还有票,就卖掉一张票
System.out.println(Thread.currentThread().getName()+"卖掉了1张票,剩余票数为:"+ticketsCont);
}
}*/
}
public class TicketsRunnable
{
public static void main(String args[]){
MyThread2 mt=new MyThread2();
//创建三个线程来模拟三个售票窗口
Thread th1=new Thread(mt,"窗口1");
Thread th2=new Thread(mt,"窗口2");
Thread th3=new Thread(mt,"窗口3");
//启动三个线程,也即是三个窗口,开始卖票
th1.start();
th2.start();
th3.start();
}
}
场景:一个主线程,一个守护线程,守护线程会在很长一段时间内向本地文件中写入数据,主线程进入阻塞状态等待用户的输入,一旦确认了用户的输入阻塞就会解除掉,主线程继续运行直到结束,守护线程也会随虚拟机一同结束。
import java.io.*;
import java.util.Scanner;
class Daemon implements Runnable
{
@Override
public void run(){
System.out.println("进入守护线程");
try{
writeToFile();
}
catch(Exception e){
e.printStackTrace();
}
System.out.println("退出守护线程");
}
private void writeToFile() throws Exception{
File filename=new File("F:/慕课网(imooc)/细说多线程之Thread VS Runnable/daemon.txt");
OutputStream os=new FileOutputStream(filename,true);
int count=0;
while(count<999){
os.write(("\r\nword"+count).getBytes());
System.out.println("守护线程"+Thread.currentThread().getName()+"向文件中写入word"+count++);
Thread.sleep(1000);
}
}
}
public class DaemonThreadDemo
{
public static void main(String args[]){
System.out.println("进入主线程"+Thread.currentThread().getName());
Daemon daemonThread=new Daemon();
Thread thread =new Thread(daemonThread);
thread.setDaemon(true);
thread.start();
Scanner sc=new Scanner(System.in);
sc.next();
System.out.println("退出主线程"+Thread.currentThread().getName());
}
}
建议使用Runnable这种方式创建线程。 程序中的同一资源指的是同一个Runnable对象。安全的卖票程序中需要加入同步synchronized。