进程本质上是一个执行的程序。
每个进程都有自己独立的一块内存空间、一组系统资源。在进程概念中,每一个进程的内部数据和状态都是完全独立的。
线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制,但与进程进程不同的是,同类的多个线程是共享同一块内存空间和一组内存空间和一组系统资源的,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈。
多线程是指在单个程序中可以同时运行多个不同的线程,执行不同的任务。多线程意味着一个程序的多行语句可以看上去几乎在同一时间内同时运行。
要产生一个线程,有两种方法:
1.需要从java.lang.Thread类派生一个新的线程类,重载它的run()方法。
2.实现Runnable接口,重载Runnable接口中的run()方法。
class NewThread extends Thread
{
NewThread(){
super("Thread Demo");
System.out.println("New thread :"+getName());
}
public void run(){
while(true){
System.out.println(Thread.currentThread().getName()+" is running");//静态方法
}
}
}
class ThreadDemo{
public static void main(String[] args){
NewThread thd = new NewThread();
thd.start();
}
}
=================================================================================================================
class NewThread implements Runnable{
public void run(){
for(int i = 10;i>0;i--){
try{
System.out.println("left time ;"+i);
Thread.sleep(1000);
}catch(InterruptedException e){
System.out.println(e.getMessage());
}
}
System.out.println("game is over,bye!");
}
}
class ThreadDemo{
public static void main(String [] args){
NewThread newthread = new NewThread();
Thread thd = new Thread(newthread,"Thread Demo");
thd.start();
}
}
由于操作系统在调度线程的时候并不是按顺序进行的,而是将时间片轮流分给每一个线程,具体每一次分给哪个线程也是不确定的,但可以保证在很短的时间内,每一个线程都有机会被执行的机会。
火车票问题,用线程的方式来解决
class MutliThread implements Runnable{
private int ticket =100;
public void run(){
while(ticket>0){
System.out.println(ticket -- +" is saled by "+Thread.currentThread().getName());
}
}
}
class MutliThreadDemo{
public static void main(String [] args){
MutliThread m = new MutliThread();
Thread t1 = new Thread(m,"window 1");
Thread t2 = new Thread(m,"window 2");
Thread t3 = new Thread(m,"window 3");
t1.start();
t2.start();
t3.start();
}
}
Java中,线程的优先级是介于Thread.MIN_PRIORITY到Thread.MAX_PRIORITY这两个常量之间的某个整数数值(1-10)。默认情况下,线程的优先级都是5,在java中用NORM_PRIORITY来表示。其中,MIN_PRIORITY,MAX_PRIORITY,NORM_PRIORIYT均是Thread类的静态整型常量。
当利用某一线程又创建了一个新线程对象时,这个新线程将拥有与创建它的线程一样的优先级。
线程的生命周期
1.创建状态 当一个线程被实例化后,它就处于创建状态,直到调用start()方法。当一个线程处于创建状态时,它仅仅是一个空的线程对象,系统不为它分配资源。在这个状态下,线程还不是活的。
Thread myThread = new MyThreadClass();
2.可运行状态 对于处于创建状态的线程来说,只要对其调用start()方法,就可以使该线程进入可运行状态。当一个线程处于可运行状态时,系统为这个线程分配它所需要的系统资源,然后安排其运行并调用线程的run()方法。此时的线程具备了所有能够运行的条件,但是,此状态的线程并不一定马上就被执行。这是因为目前所使用的计算机大多都是单处理器的,因此,要想在同一时刻运行所有的处于运行状态的线程是不可能的。
myThread.start();
3.不可运行状态
可以认为,在下面列出的条件之一发生时,线程进入不可运行状态。
(1)调用了sleep()方法;
(2)为等候一个条件变量,线程调用了wait()方法;
(3)输入输出流中线程被阻塞。
不可运行状态也可以称为阻塞状态(Blocked)。如:sleep的时间到达、得到条件变量之后使用了notify()方法或者notifyAll()方法唤醒了waiting中的一个或所有线程、获得了需要的I/O资源等。
4.消亡状态
一般来讲,线程的run()方法执行完毕时,该线程就被视为进入了消亡状态。一个处于消亡状态的线程不能再进入其他状态,即使对它调用start()方法也无济于事。可以通过两种方法使线程进入消亡状态:自然撤销(线程执行完)或是强行中止。
等待一个线程执行结束后再运行另一个线程,有两种方法:
方法一:
class NewThread implements Runnable{
//省略
}
class ThreadTestDemo{
public static void main(String []args){
NewThread n = new NewThread();
Thread t1 = new Thread(n,"thread1");
Thread t2 = new Thread(n,"thread2");
t1.start();
while(t1.isAlive()){
try{
Thread.sleep(100);//让主线程休眠100毫秒,程序不往下执行。
}catche(InterruptedException e){
e.printStackTrace();
}
}
t2.start();
}
}
===================================================================================================================
方法二:
class NewThread implements Runnable{
//省略
}
class ThreadTestDemo2{
public static void main(String []args){
NewThread n = new NewThread();
Thread t1 = new Thread(n,"thread1");
Thread t2 = new Thread(n,"thread2");
t1.start();
try{
t1.join();//当前线程等待线程t1执行完后再继续往下执行。
}catch(InterruptedException e ){
e.printStackTrace();
}
t2.start();
}
}
线程中断:(用户线程在调用interrupt()方法后,并没有被中断,而是继续执行,直到人为地中止。m1.interrupt()语句只有当线程发生阻塞时才有效。它的作用就是抛出一个InterruptedException类的异常对象,是try...catch语句捕获异常,并对其进行处理。)
class MyThread extends Thread{
boolean stop = false;
public void run(){
while(!stop){
System.out.println(getName()+" is running");
try{
sleep(1000);
}catch(InterruptedException e ){
e.printStackTrace();
}
}
System.out.println("Thread is exiting...");
}
}
class InterruptThreadDemo{
public static void main(String [] args){
MyThread m = new MyThread();
System.out.println("Starting thread ...");
m.start();
Thread.sleep(3000);
System.out.println("Interrupt thread...");
m.stop = true;//修改共享变量
Thread.sleep(3000);//主线程休眠以观察线程中断后的情况
System.out.println("Stopping application ...");
}
}
同步:
情况一:使用同步代码块
class SaleTickets implements Runnable{
private String ticketNo = "100750";//车票编号
private int ticket = 1;//共享私有成员,编号为100750的车票数量为1
public void run(){
System.out.println(Thread.CurrentThread().getName() + " is saling Thicket"+ticketNo);//
//下面同步代码块中的代码为关键代码,用synchronized关键字来标识
synchronized(this){
if(ticket>0){
try{
Thread.sleep((int)(Matn.random()*1000));
}catch(InterruptedException e){
e.printStackTrace();
}
ticket = ticket - 1;
System.out.println("ticket is saled by "+ Thread.currentThread().getName()+",amount is :"ticket);
}
else{
System.out.println("Sorry "+Thread.currentThread().getName()+", Ticket" +ticketNo +"is saled");
}
}
}
}
class SynchronizedDemo{
public static void main(String [] args){
SaleTicket m = new SaleTicket();
Thread t1 = Thread(m,"System1");
Thread t2 = Thread(m,"System2");
t1.start();
t2.start();
}
}
情况二:使用同步方法
class SaleTickets implements Runnable{
private String ticketNo = "100750";//车票编号
private int ticket = 1;//共享私有成员,编号为100750的车票数量为1
public void run(){
System.out.println(Thread.CurrentThread().getName() + " is saling Thicket"+ticketNo);//
//下面同步代码块中的代码为关键代码,用synchronized关键字来标识
sale();
}
public synchronized void sale(){ //同步方法中的代码为关键代码
if(ticket>0){
try{
Thread.sleep((int)(Matn.random()*1000));
}catch(InterruptedException e){
e.printStackTrace();
}
ticket = ticket - 1;
System.out.println("ticket is saled by "+ Thread.currentThread().getName()+",amount is :"ticket);
}
else{
System.out.println("Sorry "+Thread.currentThread().getName()+", Ticket" +ticketNo +"is saled");
}
}
}
class SynchronizedDemo{
public static void main(String [] args){
SaleTicket m = new SaleTicket();
Thread t1 = Thread(m,"System1");
Thread t2 = Thread(m,"System2");
t1.start();
t2.start();
}
}
这两种方法的区别:
简单地说,用synchronized关键字修饰的方法不能被继承。或者说,如果父类的某个方法使用了synchronized关键字修饰,那么在其子类中该方法的重载方法是不会继承其同步特征的。如果需要在子类中实现同步,应该重新使用synchronized关键字来修饰。
注意:在多线程的程序中,虽然可以使用synchronized关键字来修饰需要同步的方法,但是并不是每一个方法都可以被其修饰的。比如,不要同步一个线程对象的run()方法,因为每一个线程运行都是从run()方法开始的。在需要同步的多线程程序中,所有线程共享这一方法,由于该方法又被synchronized关键字所修饰,因此一个时间内只能有一个线程能够执行run()方法,结果所有线程都必须等待前一个线程结束后才能执行。
死锁问题:即由于两个或多个线程都无法得到相应的锁二造成的两个线程都等待的现象。这种现象主要是因为相互嵌套synchronized代码段而造成的。
共享池解决生产者和消费者问题
class SyncStack{
private int index = 0;
private char[] buffer = new char[5];
public synchronized void push(char c){
if(index == buffer.length){
try{
this.wait();
}catch(InterruptedException e ){
e.printStackTrace();
}
}
buffer[index] = c;
index++;
this.notify();
}
public synchronized char pop(){
if(index == 0){
try{
this.wait();
}catch(InterruptedException e ){
e.printStackTrace();
}
}
this.notify();
index --;
return buffer[index];
}
}
class Producer implements Runner{
SyncStack s;
public Producer(SyncStack s){
this.s = s;
}
public void run(){
char ch;
for(int i = 0;i<5;i++){
try{
Thread.sleep((int)(Math.random()*1000));
}catch(InterruptedException e){
e.printStackTrace();
}
ch = (char)(Math.random()*26+A);
s.push(ch);
System.out.println("Push" + ch +"in Stack");
}
}
}
class Consumer implements Runnable{
SyncStack s ;
public Consumer(SyncStack s){
this.s = s;
}
public void run(){
char ch;
for(int i=0;i<5;i++){
try{
Thread.sleep((int)(Math.random()*3000));
}catch(InterruptedException e){
e.printStackTrace();
}
ch =s.pop();
System.out.println("Pop" + ch +" from stack");
}
}
}
public class Communication{
public static void main(String[] args){
SyncStack stack = new SyncStack();
Thread t1 = new Thread(new Producer(stack));
Thread t2 = new Thread(new Producer(stack));
t1.start();
t2.start();
}
}