1.进程:正在执行的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫做一个控制单元。
2.线程:就是进程中的一个独立的控制单元;线程在控制着进程的执行。
一个进程至少有一个线程。
3.线程的状态:创建,运行,冻结,阻塞,消亡。
4.线程的一些API函数
static ThreadcurrentThread():获取当前线程对象
getName():获取线程名称
setName():设置线程名称或者构造函数
5.多线程的创建方式
1>继承Thread类:
a>定义类继承Thread
b>复写Thread中的run()方法
c>调用线程的start方法:启动线程,调用run()方法
class Demo extends Thread //继承Thread
{
//复写run方法
public void run()
{
for(int x=0; x<60; x++)
System.out.println("demorun..."+x);
}
}
class ThreadDemo
{
public static voidmain(String[] args)
{
Demo d = new Demo();
d.start();//调用start方法,启动线程
for(int x=0; x<60; x++)
System.out.println("mainrun..."+x);
}
}
2>实现Runnable接口
a>定义类实现Runnable接口
b>覆盖Runnable接口中的run方法
c>通过Thread类建立线程对象
d>将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法
e>调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
/*
* 创建线程方式二:实现Runnable接口
*
*/
class Ticket implements Runnable
{
private int tick = 100;
public void run()
{
while(true)
{
if(tick > 0)
System.out.println(Thread.currentThread().getName()+"..sale..."+tick--);
}
}
}
public class ThreadDemo2 {
public static voidmain(String[] args) {
// TODO Auto-generated method stub
//创建实现Runnable接口子类Ticket对象
Ticket t = new Ticket();
Thread t1 = new Thread(t);//创建接收t的Thread对象t1
Thread t2 = new Thread(t);//创建接收t的Thread对象t2
t1.start();//开启线程
t2.start();//开启线程
}
}
实现接口方式和继承方式有什么区别?
a>实现方式的好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
b>继承方式:线程代码存放在Thread子类run方法中
实现方式:线程代码存放在接口的子类的run方法中
6.多线程安全问题
1>产生安全原因:当一个线程在执行共享数据时,还未执行完,另一个线程参与进来, 导致共享数据的错误。
解决方法:每时每刻只让一个线程执行共享数据,当其未执行完,其它线程不可以参 与执行。
2>解决多线程安全问题方法
a>同步代码块
格式://该对象即为同步监视器,也就是所谓的锁
//锁可以使用object的对象,如Object obj = new Object();
synchronized(对象)
{
需要被同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行。没有锁的线程即使获取cpu执行权,也进不去,因为没有获取到锁。
class Bank
{
private int sum;
private Object obj = newObject();//定义同步代码块的锁
public void add(int num)
{
//同步代码块
synchronized(obj)
{
sum = sum + num;
try{Thread.sleep(10);}catch(InterruptedExceptione){}
System.out.println("sum="+sum);
}
}
}
class Cus implements Runnable
{
private Bank b = newBank();
public void run()
{
for(int x=0; x<3; x++)
{
b.add(100);
}
}
}
class BankDemo
{
public static voidmain(String[] args)
{
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
同步前提:1)必须要有两个或者两个以上的线程。
2)必须是多个线程使用同一个锁。
同步代码块的利弊:
利:可以解决多线程的安全性
弊:多个线程都要判断锁,消耗资源
哪些代码同步?:明确哪些代码是多线程运行代码;明确共享数据;明确多线程运 行代码中哪些语句是操作共享数据。
b>同步函数
格式:直接将synchronized放到函数上进行修饰
例:public synchronized void add()
将上述代码中的Bank类按下更改,即可通过同步函数解决多线程访问安全
class Bank
{
private int sum;
public synchronized voidadd(int num)//同步函数
{
sum = sum + num;
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println("sum="+sum);
}
}
同步函数使用的锁是:this
静态同步函数使用的锁是:类名.class(字节码文件对象)该对象的类型是class
7.线程间通信
1>线程间通信:其实就是多个线程在操作一个资源,但是操作的动作不同。
2>等待唤醒机制
a>wait,notify,notifyall都要使用在同步中(synchronized),因为要对持有监视器(锁)的线程操作。只有同步才具有锁。
wait和sleep区别?
1)wait可以指定时间也可以不指定。
sleep必须指定时间。
2)在同步中时,对cpu的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
等待和唤醒必须是同一个锁。
class Res
{
private String name;
private int count = 1;
private boolean flag =false;//定义标志,用于进程间切换
public synchronized voidset(String name)
{
//使用while判断,让被唤醒的线程再一次判断
while(flag)//if(flag)
try{this.wait();}catch(Exceptione){}//wait等待,锁是this
this.name = name+"-->"+count++;
System.out.println("生产者.."+this.name);
flag = true;
this.notifyAll();//唤醒全部线程//this.notify
}
public synchronized voidout()
{
//使用while判断,让被唤醒的线程再一次判断
while(!flag)//if(flag)
try{this.wait();}catch(Exceptione){}//wait等待,锁是this
System.out.println("消费者.."+this.name);
flag = false;
this.notifyAll();//唤醒全部线程//this.notify
}
}
class Producer implements Runnable
{
private Res r;
Producer(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("商品");
}
}
}
class Consumer implements Runnable
{
private Res r;
Consumer(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
class ProducerConsumerDemo
{
public static voidmain(String [] args)
{
Res r = new Res();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
//开启四个线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
b>Lock接口与Condition接口(也是JDk1.5的新特性)
Lock替代了synchronized;而Condition替代了Object中的监视器方法(wait,notify,notifyall)使用,Condition可以通过Lock锁获取,一个Lock可以对应多个Condition。
与之对应上面代码,我们只要更改Res类中代码如下:
class Res
{
private String name;
private int count = 1;
private boolean flag =false;//定义标志,用于进程间切换
private Lock lock = newReentrantLock();
private Condition con =lock.newCondition();
//private Condition con_pro =lock.newCondition();
//private Condition con_con =lock.newCondition();
//此处抛了异常,在处理线程时一定要记得做异常处理
public void set(Stringname)throws InterruptedException
{
lock.lock();//上锁,lock()与unlock()其实就类似于synchronized
try{
while(flag)
con.await();//线程等待,类似于wait
//con_pro.await();
this.name =name+"-->"+count++;
System.out.println("生产者.."+this.name);
flag = true;
con.signalAll();//唤醒线程,类似于notifyAll
//con_con.signal();
}
finally{
lock.unlock();//释放锁
}
}
//此处抛了异常,在处理线程时一定要记得做异常处理
public void out()throwsInterruptedException
{
lock.lock();//上锁
try{
while(!flag)
con.await();//线程等待
//con_con.await();
System.out.println("消费者.."+this.name);
flag = false;
con.signalAll();//唤醒线程
//con_pro.signal();
}
finally{
lock.unlock();//释放锁
}
}
}
//以上注释的代码,实现了等待本方线程和只唤醒对方线程的功能。
8.停止线程
stop方法已经过时,run方法结束才可以停止线程
1>定义循环结束标记
因为线程运行的代码一般都是循环,只要控制了循环即可。
2>使用interrupt(中断)方法
该方法是结束线程的冻结状态,使线程回到运行状态中来。
9.join:抢夺cpu执行权
当A线程执行到B线程的.join()方法是,A就会等待。等待B线程执行完后A才会执行。
join可以用来临时加入线程执行。