一、创建线程方式
1.创建线程有两种方式可以实现:继承Thread类或实现Runnable接口,线程类对象调用start()方法启动线程执行线程类的run()方法。
2.Thread类的子类创建线程,重写run方法然后就可以使用该类创建对象也就是一个线程对象调用父类的start方法线程开始执行。一个简单的例子:
class MyThread extends Thread
{
private String name;
public MyThread()
{
}
public MyThread(String name)
{
this.name = name;
}
@Override
public void run()
{
for (int i = 0; i < 5; i++)
{
System.out.println(name+"线程..."+i);
}
}
}
public static void main(String[] args)
{
MyThread mtha = new MyThread("a");
MyThread mthb = new MyThread("b");
mtha.start();
mthb.start();
}
结果是没有固定顺序的,因为两个线程相互抢占cpu资源。
a线程...0
b线程...0
b线程...1
b线程...2
b线程...3
b线程...4
a线程...1
a线程...2
a线程...3
a线程...4
3.Thread线程类的方法:
public final static getName()获得线程的名字。
public final void setPriority(int newPriority)方法设置线程的优先级从1---10优先级一次增高。
public static Thread currentThread()类方法可以获取当前正在执行的线程。
interrupt方法是Thread类的一个静态方法,当调用该方法时可以使当前对象的线程中断执行。
yield方法是Thread的一个静态方法,调用该方法时可以让线程在这个时间片暂停让其他线程执行,当到下一个时间片时继续执行原来的线程。
sleep方法是Thread的一个静态方法,调用该方法时可以让当前线程阻塞一段时间该线程并不释放cpu的资源。
wait方法是Object的方法,当调用该方法时停止当前线程执行把线程放到对象的等待队列中当执行该对象调用notify或notifyAll方法时原来的线程才会继续执行。wait和notify主要用在生产者和消费者关系中。且wait、notify、notifyAll方法都只能用在同步块中或同步方法中。
4.通过Runnable接口创建线程,然后用Runnable接口的实现类创建对象,使用Thread(Runnable target)带参数的构造方法创建线程。一个简单的例子:
class MyRunnable implements Runnable
{
public String name;
public MyRunnable()
{
}
public MyRunnable(String name)
{
this.name = name;
}
public void run()
{
for (int i = 0; i < 10; i++)
{
System.out.println(name+"..."+Thread.currentThread().getName()+"..."+i);
}
}
}
public static void main(String[] args)
{
MyRunnable mr = new MyRunnable("A");
Thread th1 = new Thread(mr);
Thread th2 = new Thread(mr);
th1.start();
th2.start();
}
}
5.线程同步:当一段代码有一个线程进去执行时我们就不希望再有其他的线程进去执行,这时我们就需要对这部分代码加锁及当一个线程进去执行这段代码时就在这段代码前加上锁,不让其他进程进来当这个线程执行完时就把锁解开让其他线程进来执行。
同步代码块用synchronized(Object obj){...}包起来,实现当一个线程在执行这部分代码时其他线程不能进去,也可以将这部分代码提取出来写成函数及同步方法,同步方法用synchronized修饰。
对象锁:obj对象可以是任何一个对象,通常情况下使用this及方法所在的对象。静态方法进行同步时所用的锁是静态方法所在的类对应class对象的锁。
6.每一个对象除了有一个对象锁之外还有一个等待队列(wait set),当对象刚创建时它的等待队列是空的,我们应该在当前线程锁住对象后,去调用该对象的wait方法把再想进入同步块里执行的线程放入等待队列中。当上一线程执行完成时调用对象的notify方法将任意一个线程从等待队列中删除,这个线程将再次成为可运行的线程。当对象调用notifyAll时将从对象的等待队列中删除所有等待线程,这些线程将重新成为可以运行的线程。
wait和notify/notifyAll方法主要用于producer---consumer(生产者---消费者)关系中且wait、notify、notifyAll方法只能使用在同步块或同步方法中,这三个方法是继承自Object类的方法。
二、线程的四种设计方式
1.多个线程共享资源做相同的事
通过实现Runnable接口创建线程,接口的实现类实例化对象MyThread mt = new MyThread();再用Thread类含有参数的构造方法去创建三个线程
newThread(mt).start();
newThread(mt).start();
newThread(mt).start();
因为这三个线程都是通过同一个对象创建的,也就是说都可以操作对象里面的成员变量(如多个代售点同时售票)
还有一种方法使用内部类继承Thread类也可以实现多个线程共享资源如:
public class MyThread
{
int index = 0;
private class InnerThread extends Thread
{
@Override
public void run()
{
for(int i = 0;i<5;i++)
{
index += i;
System.out.println(Thread.currentThread().getName()+"..."+index);
}
}
}
public InnerThread getThread()
{
return new InnerThread();
}
public static void main(String[] args)
{
MyThread mt = new MyThread();
mt.getThread().start();
mt.getThread().start();
mt.getThread().start();
}
}
因为每次调用mt.getThread().start();就会创建一个内部类的对象也就是一个线程,又因为内部类可以操作外部类的变量和函数所有内部类产生的多个线程对象改变的是同一个对象的成员变量。
2.多个线程共享资源做不同的事
共享资源以参数的形式传入到线程类中,两个线程的run方法分别实现不同的操作(生产者---消费者)
比如有两个人一个向树洞中放入信件,另一个人从树洞中取出信件,当树洞中没有信件时就要等放的人先放入再取,当树洞中有信件时就要等取的人先取走再放。
class Q
{
String name = "unKnown";
String sex = "unknown";
boolean bfull = false;
}
class Producer implements Runnable{
Q q;
public Producer(Q q)
{
this.q = q;
}
@Override
public void run()
{
while(true)
{
synchronized (q)
{
if(q.bfull)//如果树洞中有信的就让生产者等待
{
try
{
q.wait();
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}
q.name = "张三";
q.sex = "男";
q.bfull = true;
System.out.println("树洞中放入信件");
q.notify();
}
}
}
}
class Consumer implements Runnable
{
Q q;
public Consumer(Q q)
{
this.q = q;
}
@Override
public void run()
{
while(true)
{
synchronized (q)
{
if(!q.bfull) //如果树洞里面没有信件就等
{
try
{
q.wait();
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}
System.out.println("取出树洞中的信件"+q.name+"..."+q.sex);
q.name = "unKonwn";
q.sex = "unkonwn";
q.bfull = false;
q.notify();
}
}
}
}
public static void main(String[] args)
{
Q q = new Q();
Producer pro = new Producer(q);
Consumer con = new Consumer(q);
new Thread(pro).start();
new Thread(con).start();
}
在共享资源所在的类中实现同步的不同操作,然后将资源分别传到不同的线程中,不同线程的run方法调用资源对象的不同方法,则不同线程执行不同方法
class Info
{
String name = "unKnown";
String sex = "unKnown";
boolean bfull = false;
public synchronized void setInfo()
{
if(bfull) //如果树洞里面已经有信件就放信线程就要等待
{
try
{
this.wait();
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}
this.name = "张三";
this.sex = "男";
this.bfull = true;
System.out.println("放入信件....");
this.notify();
}
public synchronized void getInfo()
{
if(!bfull) //如果树洞中没有信件就要等放入信件才能取
{
try
{
this.wait();
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}
System.out.println("树洞中取出信件"+this.name+"...."+this.sex);
this.name = "unKnown";
this.sex = "unKnown";
this.bfull = false;
this.notify();
}
}
class Produce extends Thread
{
Info info;
public Produce(Info info)
{
this.info = info;
}
@Override
public void run()
{
while(true)
{
info.setInfo();
}
}
}
class Consume extends Thread
{
Info info;
public Consume(Info info)
{
this.info = info;
}
@Override
public void run()
{
while(true)
{
info.getInfo();
}
}
}
public static void main(String[] args)
{
Info info = new Info();
Produce pro = new Produce(info);
Consume con = new Consume(info);
pro.start();
con.start();
}
大家可以体会两种实现生产者和消费者问题的方式有什么不同。