基本概念:
@程序是计算机指令的集合,它以文件的形式存储在磁盘上。
@进程:是一个程序在其自身的地址空间中的一次执行活动。
进程是资源申请、调度和独立运行的单位,因此,它使用系统中的运行资源;而程序不能申请系统资源,不能被系统调度,也不能作为独立运行的单位,因此,它不占用系统的运行资源。
@线程:是进程中的一个单一的连续控制流程。一个进程可以拥有多个线程。
线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。
@后台线程的概念:main 方法是一个非后台线程,当一个程序没有非后台线程在运行的时候,jvm就要退出了.可以使用线程对象的setDaemon()来设置后线程.
yield() 方法可暂是放弃线程的执行权力.
线程的优先级 0(Thread.MIN_PRIORITY).5(Thread.NORM_PRIORITY),10(Thread.MAX_PRIORITY)
Java在语言级提供了对多线程程序设计的支持。
实现多线程程序的两种方式:
(1)从Thread类继承;
package cn.com.xinli.test.miti;
public class TestMitiThread
{
public static void main(String[] rags) throws Exception
{
MitiSay ms= new MitiSay("A");
ms.start();
System.out.println(Thread.currentThread().getName());
}
}
class MitiSay extends Thread //implements Runnable
{
public MitiSay(String name)
{
super(name);
}
public void run()
{
System.out.println("线程"+Thread.currentThread().getName());
}
}
(2)实现Runnable接口。
public class TestMitiThread
{
public static void main(String[] rags) throws Exception
{
MitiSay ms= new MitiSay();
new Thread(ms).start();
System.out.println(Thread.currentThread().getName());
}
}
class MitiSay implements Runnable
{
public void run()
{
System.out.println("线程"+Thread.currentThread().getName());
}
}
让多个线程访问一个共享的变量:
方式1.使用Runnable 接口实现,因为继承Thread,多个线程就会有多个变量的拷贝
方式2.使用内部类,可以随意的访问外部类的变量和方法,记得在外部类中写一个得到内部类的方法就ok了
public class TestMitiThread
{
public static void main(String[] rags) throws Exception
{
MitiSay ms=new MitiSay();
ms.getInnerThread().start();
ms.getInnerThread().start();
ms.getInnerThread().start();
ms.getInnerThread().start();
}
}
class MitiSay // implements Runnable
{
int index=100;
class InnerThread extends Thread
{
public void run()
{
while(true)
{
if(index<0)
break;
System.out.println("线程"+Thread.currentThread().getName()+":"+index--);
// Thread.yield();
}
}
}
public InnerThread getInnerThread()
{
return new InnerThread();
}
}
以上两中方法都可以打印出联系减少的index变量的值,并且是由不同的线程打印的,时间了多个线程访问一个共享的变量
多线程模拟火车站售票系统:
public class TicketsSystem
{
public static void main(String[] args)
{
SellThread st=new SellThread();
new Thread(st).start();
new Thread(st).start();
new Thread(st).start();
new Thread(st).start();
}
}
class SellThread implements Runnable
{
int tickets=100;
public void run()
{
while(true)
{
if(tickets>0)
{
/*
try
{
Thread.sleep(1);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
*/
System.out.println(Thread.currentThread().getName()+":sell "+tickets);
tickets--;
}
}
}
}
程序打印出从100到1,4个线程卖出的票,但是这个程序有一个潜在的问题,就是在长时间的运行下,有可能打印出0,-1,-2这样的票号,为了重现这种情况,我们把注视的代码打开就运行就可以看到了,也就是说一个线程判断玩剩余的票数之后休眠了,而其他的线程此时却修改了 tickets 变量,然后tickets -- 则会出现0,-1,-2 的票号.原因:是由于多个线程共同访问和修改了 共享的变量 tickets
同步的两种方式:同步块和同步方法
每一个对象都有一个监视器,或者叫做锁。
同步方法利用的是this所代表的对象的锁。
每个class也有一个锁,是这个class所对应的Class对象的锁。
1.使用同步块技术 保护 关键代码
public class TicketsSystem
{
public static void main(String[] args)
{
SellThread st=new SellThread();
new Thread(st).start();
new Thread(st).start();
new Thread(st).start();
new Thread(st).start();
}
}
class SellThread implements Runnable
{
int tickets=100;
Object o=new Object();
public void run()
{
while(true)
{
synchronized(o)
{
if(tickets>0)
{
try
{
Thread.sleep(1);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":sell "+tickets);
tickets--;
}
}
}
}
}
2.使用同步方法
public class TicketsSystem
{
public static void main(String[] args)
{
SellThread st=new SellThread();
new Thread(st).start();
new Thread(st).start();
new Thread(st).start();
new Thread(st).start();
}
}
class SellThread implements Runnable
{
int tickets=100;
Object o=new Object();
public void run()
{
while(true)
{
sell();
}
}
public synchronized void sell()
{
if(tickets>0)
{
try
{
Thread.sleep(1);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":sell "+tickets);
tickets--;
}
}
}
@每一个对象除了有一个锁之外,还有一个等待队列(wait set),当一个对象刚创建的时候,它的对待队列是空的。
@我们应该在当前线程锁住对象的锁后,去调用该对象的wait方法,只能在同步方法和同步块钟使用
@当调用对象的notify方法时,将从该对象的等待队列中删除一个任意选择的线程,这个线程将再次成为可运行的线程。
@当调用对象的notifyAll方法时,将从该对象的等待队列中删除所有等待的线程,这些线程将成为可运行的线程。
@wait和notify主要用于producer-consumer这种关系中
生产者-消费者:利用wait()和notity(),必须使用同一个监视器(this),必须在同步方法和同步块中使用
public class ProductAndConsumer
{
public static void main(String[] args)
{
Queue q=new Queue();
Product p=new Product(q);
Consumer c=new Consumer(q);
p.start();
c.start();
}
}
class Queue
{
int value;
boolean bFull=false;
public synchronized void put(int i)
{
if(!bFull)
{
value=i;
bFull=true;
notify();
}
try
{
wait();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public synchronized int get()
{
if(!bFull)
{
try
{
wait();
}
catch(Exception e)
{
e.printStackTrace();
}
}
bFull=false;
notify();
return value;
}
}
class Consumer extends Thread
{
Queue q;
Consumer(Queue q)
{
this.q=q;
}
public void run()
{
while(true)
{
System.out.println("Consumer get "+q.get());
}
}
}
class Product extends Thread
{
Queue q;
public Product(Queue q)
{
this.q=q;
}
public void run()
{
for(int i=0;i<10;i++)
{
q.put(i);
System.out.println("Product"+"put "+i);
}
}
}
线程的状态变换
@线程的终止:
设置一个flag变量。
结合interrupt()方法。
方式一:设置一个flag变量。
class StopThread
{
public static void main(String[] args)
{
Thread1 t1=new Thread1();
t1.start();
int index=0;
while(true)
{
if(index++==500)
{
t1.stopThread();
break;
}
System.out.println(Thread.currentThread().getName());
}
System.out.println("main() exit");
}
}
class Thread1 extends Thread
{
private boolean bStop=false;
public void run()
{
while(!bStop)
{
System.out.println(getName());
}
}
public void stopThread()
{
bStop=true;
}
}
方式一:设置一个flag变量 结合interrupt()方法。
方式1,如果有业务需要在run()方法中调用了wait()方法,那我们上边的线程就不能终止了,因为它一直处于等待状态,则需要我们使用结合interrupt()方法来终止线程了
class StopThread
{
public static void main(String[] args)
{
Thread1 t1=new Thread1();
t1.start();
int index=0;
while(true)
{
if(index++==500)
{
t1.stopThread();
/*调用这个方法会抛异常*/
t1.interrupt();
break;
}
System.out.println(Thread.currentThread().getName());
}
System.out.println("main() exit");
}
}
class Thread1 extends Thread
{
private boolean bStop=false;
public synchronized void run()
{
while(!bStop)
{
try {
wait();
} catch (InterruptedException e)
{
System.out.println("由于调用了interrupt所以抛出异常");
if(bStop)
return;
}
System.out.println(getName());
}
}
public void stopThread()
{
bStop=true;
}
}