线程与进程
1 进程是指运行中的应用程序,每一个进程都有自己独立的内存空间。对一个应用程序可以同时启动多个进程。
2 线程是指进程中的一个执行流程,有时也称为执行情景。一个进程可以由多个线程组成,即在一个进程中可以同时运行多个不同的线程,它们分别执行不同的任务。当进程内的多个线程同时运行,这种运行方式称为并发运行。
3 进程消失了,线程还存在么?答:不在
4 线程消失了,进程还会存在么?答:可能在
5 线程依附进程 比进程小,不同进程的线程不可比较大小
JVM 对于一个java系统,至少要启动两个线程main gc
java.lang.Thread
public static Thread currentThread()
public final String getName()
class Test
{ public static void main(String args[])
{ System.out.println(Thread.currentThread().getName()); }
}
线程的创建和启动方法
1 定义一个Thread类的子类,覆盖Thread类的run()方法,然后创建该子类的实例。
2 定义一个实现Runnable接口的类,实现它的run()方法,然后将这个类的实例作为Thread的构造方法的参数,创建Thread类的实例。
3 两种创建线程的方法是有区别的,对于共享资源来说,最好用实现runnable接口的方法
class TT extends Thread//线程的第一种创建方法
{ public void run()
{ for(int i=0;i<10;i++)
System.out.println(Thread.currentThread().getName());}
}
class Test-9.1
{ public static void main(String[] args)
{ TT t1=new TT();
t1.run();
System.out.println(Thread.currentThread().getName()+";");}
}
//输出11个main
class TT extends Thread
{ public void run()
{ for(int i=0;i<10;i++)
System.out.println(Thread.currentThread().getName()); }
}
class Test-9.2
{ public static void main(String[] args)
{ TT t1=new TT();
//t1.run();
t1.start();//线程的启动
System.out.println(Thread.currentThread().getName()+";");}
}
//输出main;和10个Thread-0
class TT extends Thread
{ public void run()
{ for(int i=0;i<10;i++)
System.out.println(Thread.currentThread().getName()); }
}
class Test-9.3
{ public static void main(String[] args)
{ TT t1=new TT();
TT t2=new TT();
TT t3=new TT();
t1.setName("huang");
t1.start(); t2.start(); t3.start();
System.out.println(Thread.currentThread().getName()+";");}
}
class TT implements Runnable//线程的第二种创建方法
{ public void run()
{ for(int i=0;i<10;i++)
System.out.println(Thread.currentThread().getName());}
}
class Test-9.4
{ public static void main(String[] args)
{ Runnable t=new TT();
Thread t1=new Thread(t);
t1.start();
System.out.println(Thread.currentThread().getName()+";");}
}
//输出main;和10个Thread-0
class TT extends Thread//实现售票
{ /*static*/ int tickets=20;
public void run()
{ //int tickets=20;错误
for (int i=0;i<40;i++)
{ if (tickets>0)
{System.out.println(Thread.currentThread().getName()
+";"+tickets--); }}} }
class Test-9.5
{ public static void main(String args[])
{ TT t1=new TT(); t1.setName("1");
TT t2=new TT(); t2.setName("2");
TT t3=new TT(); t3.setName("3");
t1.start(); t2.start(); t3.start();
System.out.println(Thread.currentThread().getName()
+"*****");} }
class TT implements Runnable//实现售票
{ /*static*/ int tickets=20;
public void run()
{ //int tickets=20;错误
for (int i=0;i<40;i++)
{ if (tickets>0)
{System.out.println(Thread.currentThread().getName()
+";"+tickets--); }}} }
class Test-9.6
{ public static void main(String args[])
{ TT t1=new TT(); /*TT t2=new TT();TT t3=new TT();*/
new Thread(t1,"1").start();
new Thread(t1,"2").start();
new Thread(t1,"3").start();
System.out.println(Thread.currentThread().getName()
+"*****");} }
class TT implements Runnable
{ static int tickets=20;
public void run()/*throws Exception 错误*/
{ for(int i=0;i<40;i++)
{ if(tickets>0)
{try { Thread.sleep(500);}
//只是让线程执行速度减缓,不影响代码执行本身
catch(Exception e){}
System.out.println(Thread.currentThread().getName()
+":"+tickets--);}}
}
}
class Test-9.7
{ public static void main(String[] args) throws Exception
{ TT t1=new TT();
TT t2=new TT();
TT t3=new TT();
new Thread(t1,"1站").start();
new Thread(t2,"2站").start();
new Thread(t3,"3站").start();
}
//出现错误有:重复、0、-1
//线程的安全性问题(同步)
线程同步synchronized(同步代码块、同步方法)以及锁旗标
class TT implements Runnable
{ static int tickets=20;
String name="huang";
public void run()
{ for(int i=0;i<40;i++)
{ synchronized(name) //同步代码块
//synchronized(this)
{ if(tickets>0)
{try { Thread.sleep(500);}
//只是让线程执行速度减缓,不影响代码执行本身
catch(Exception e){}
System.out.println(Thread.currentThread().getName()
+":"+tickets--);}}
}
}
class Test-9.8
{ public static void main(String[] args) throws Exception
{ TT t1=new TT();
TT t2=new TT();
TT t3=new TT();
new Thread(t1,"1站").start();
new Thread(t2,"2站").start();
new Thread(t3,"3站").start();
}
//synchronized(公认对象名[锁旗标])
/*添加同步代码块对象,必须是公认的。不能在run方法里,若在run方法则无效*/
售票实例讲解
public class ProducerAndConsumer
{ /**
* 假定开始售票处并没有票,一个线程往里存票,另一个线程则往外卖票。
* 新建一个票类对象,让存票和售票线程都访问它。
* 两个线程共享同一个数据对象来实现对同一份数据的操作
*/
public static void main(String[] args)
{ Tickets t=new Tickets(10); //新建一个票类对象,总票数作为参数
new Producer(t).start(); //以票类对象为参数创建存票线程对象,并启动
new Consumer(t).start();//以同一个票类对象为参数创建售票线程,并启动
}
}
//票类
class Tickets
{ int number=0; //票号
int size; //总票数
boolean available=false; //表示目前是否有票可售
public Tickets(int size){this.size=size;}//构造函数,传入总票数参数
}
//存票线程
class Producer extends Thread
{ Tickets t=null;
public Producer(Tickets t)
{ this.t=t; }//构造函数:以一个票类为参数
public void run()
{ while(t.number<t.size)
{//限制循环条件为存票序号小于总票数
synchronized(t){//申请对象t的锁旗标
System.out.println("Producer puts ticket "+(++t.number));
t.available=true;//可以买票
}//自动释放锁旗标
} } }
//售票线程
class Consumer extends Thread
{ Tickets t=null;
int i=0;
public Consumer(Tickets t)
{ this.t=t; } //构造函数:以一个票类对象为参数
public void run()
{ synchronized(t)
{ while(i<t.size)
{ //循环条件为售票序号小于总票数
if(t.available==true && i<=t.number)
{System.out.println("consumer buys ticket "+(++i)); }
//有票可售且小于目前票序号
if(i==t.number)
{ t.available=false; } //当票已售到当前序号,则不可售
} } } }
//一个对象的锁旗标只有一个,所以利用对一个对象锁旗标的争夺,可以实现不同线程的互斥效果。当一个线程获得锁旗标后,需要改锁旗标的其他线程只能处于等待状态。
//另外, 也可以将此关键字加在方法上:
//取票方法
public synchronized void sell()
{ if(!available)
{ //如果没有存票,则售票线程等待
try{ wait(); }
catch(Exception e){ }
System.out.println("consumer buys ticket "+(number));
available=false;
notify();//售票唤醒存票线程开始存票
if(number==size)
{ number=size+1; }
//在售完最后一张票后,设置一个结束标志
//number>size表示售票结束
} }
Thread类的方法
1 currentThread() 返回当前运行的Thread对象。
2 start() 启动一个线程。
3 run() 线程体,由start()方法调用,当run()方法返回时,当前的线程结束。
4 stop() 使调用它的线程立即停止执行。
5 sleep(int n)使线程睡眠n毫秒,n毫秒后,线程可以再次运行。
6 suspend() 使线程挂起,暂停运行。
7 resume() 恢复挂起的线程,使其处于可运行状态(Runnable)。
8 yield() 将CPU控制权主动移交到下一个可运行线程。
9 setPriority() 设置线程优先级。 getPriority() 返回线程优先级。
10 setName() 设置线程的名字。 getName() 返回该线程的名字。
11 isAlive( ) 如果线程已被启动并且未被终止,那么isAlive( )返回true。如果返回false,则该线程是新创建或是已被终止的。
死锁
1 死锁是程序运行时发生的一种状态
2 产生原因:多个线程共享同一资源时,要进行加锁操作(同步),但是,过多的同步会造成死锁
3 当一个线程等待由另一个线程持有的锁,而后者正在等待已被第一个线程持有的锁时,就会发生死锁。Java不监测也不试图避免这种情况。因而保证不发生死锁就成了程序员的责任
class ST implements Runnable //死锁程序的模拟:
{ int ticket=10;
String str=new String("hh");
/*添加同步代码块对象,必须是公认的。不能在run方法里,若在run方法则无效*/
public void run()
{ if(str.equals("huang"))
{ for (int i=0;i<30 ;i++ ) { this.sale(); }
else
{ for (int i=0;i<30 ;i++ )
{ synchronized(str)
//str称为锁旗标或监视器,有0,1状态,进去后为0,出来后为1
{ if (ticket>0)
{ try{Thread.sleep(100); }
catch (Exception e){}
synchronized(this) {}
System.out.println(Thread.currentThread().getName()+" :"+ticket--);
/*ticket>0和ticket--不是原子性操作时,解决方法:(1)代码写进同步代码,synchronized()速度慢,因为synchronized()每次检查状态*/
}}} }}
public synchronized void sale()
//改为public void sale()不同步,同步方法的锁旗标为this
//买票的方法,加上synchronized,该方法就同步
{ if (ticket>0)
{ try { Thread.sleep(100);}
catch (Exception e){}
synchronized(str){}
System.out.println("i am here");
System.out.println(Thread.currentThread().getName()+" :"+ticket--);
}}}
class Test-9.10
{ public static void main(String[] args)
{ ST s=new ST();
Thread t=new Thread(s,"station1");
Thread t1=new Thread(s,"station2");
t.start();
try{Thread.sleep(100);}
catch(Exception e){}
s .str="huang";
t1.start();
}
}
对象数组
class Person //对象数组
{ String name;
int age;
int ID;
public void say()
{ System.out.println(name+";"+age); }
}
class Test-9.11
{ public static void main(String[] args)
{ Person[] p=new Person[10];//生成10个对象
//对象数组。每个单元存放的都是一个对象
Person s=new Person();
p[0]=s;
p[0].name="zhangsan"; p[0].ID=1;p[0].say();
}
内存图讲解
1 程序中用关键字new出来的东西都是存放在heap segment。
2 程序中的局部变量存放在stack segment,这些局部变量是在具体方法执行结束之后,系统自动释放内存资源(而heap segment中的资源需要java垃圾回收机制来处理)。形参一定在栈stack内存里
3 程序中的方法,是内存中的code segment中的,而且是多个对象 共享一个代码空间区域。
4 static静态变量,需要放在内存中的data segment
生产者与消费者实例讲解
//生产者与消费者问题
//涉及线程的通信
class SZP //山楂片
{ private int ID;
public SZP(int ID) {this.ID=ID;}
public String toString() {return "SZP"+ID;}
}
class RQ //容器
{ int top=0;
SZP [] sz = new SZP[10];
public synchronized void push(SZP s)
{ if(top==sz.length)
{ try{ this.wait(); }
//wait时,锁给别人,才自动释放;sleep睡觉时,锁还在。
catch (Exception e){ } }
this.notify(); //notify()唤醒,唤醒的是别人,不是自己
sz[top]=s;
top++;
}
public synchronized SZP pop()
{ if(top==0)
{ try{this.wait();}
catch (Exception e){ } }
this.notify();
top--;
return sz[top];
}
}
class Producer implements Runnable //生产者
{ RQ r=null;
public Producer(RQ r) { this.r=r;}
public void run()
{ for(int i=0;i<30;i++)
{ SZP s=new SZP(i);
r.push(s);
try{Thread.sleep(10);}
catch (Exception e){ }
System.out.println("produce: "+s); } }
}
class Consumer implements Runnable //消费者
{ RQ r=null;
public Consumer(RQ r) { this.r=r;}
public void run()
{ for(int i=0;i<30;i++)
{ SZP s=r.pop();
try{ Thread.sleep(1000);}
catch (Exception e){ }
System.out.println("consumer: "+s); } }
}
class Test-9.12
{ public static void main(String [] args)
{ RQ r=new RQ();
Producer p=new Producer(r);
Consumer c=new Consumer(r);
new Thread(p).start();
new Thread(c).start(); }
}