1、概念:线程是进程中的一个控制单元,也可以说是一个执行流程,一个进程中有多个线程
2、自定义线程:
方式一:继承Thread类,覆盖run()方法,将运行的代码放在run()方法中,新建子类对象,调用start()方法
class MyThread extends Thread
{
public void run()
{
for(int x=0; x<100; x++)
{
System.out.println("MyThread run"+x);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
for(int x=0; x<100; x++)
{
System.out.println("main run"+x);
}
new MyThread().start();
}
}
在上面的程序中,存在两个线程,一个是主线程,一个是自定义线程
方式二、实现Runnable接口,覆盖run方法,将执行的代码放在run方法中
使用Thread类创建线程对象,将实现Runnable接口的子类对象作为参数传递给Thread类的构造函数,
使用Thread类的对象调用start方法
class MyThread implements Runnable
{
public void run()
{
for (int x=0; x<100; x++)
{
System.out.println("MyThread run"+x);
}
}
}
class ThreadDemo2
{
public static void main(String[] args)
{
Thread t = new Thread(new MyThread());
t.start();
for (int x=0; x<100; x++)
{
System.out.println("main run"+x);
}
}
}
通过查阅API帮助文档
我们可以通过Thread(String name)
构造方法类自定义线程的名称
在Thread类的构造方法中,
static Thread currentThread():获取当前线程对象。
getName(): 获取线程名称
练习:创建两个线程和主线程交替运行
class MyThread extends Thread
{
MyThread(String name)
{
super(name);
}
public void run()
{
for (int x=0; x<50; x++)
{
System.out.println(this.getName()+"-----"+x);
}
}
}
class ThreadTest
{
public static void main(String[] args)
{
MyThread t1 = new MyThread("线程1");
MyThread t2 = new MyThread("线程2");
t1.start();
t2.start();
for (int x=0; x<50; x++)
{
System.out.println("主线程"+"-----"+x);
}
}
}
3、线程的状态:
4、例子:
class Ticket implements Runnable//车票的实体类,实现Runnable接口
{
private int ticketNum = 100;//总的票数
Object o = new Object();
public void run()
{
while(true)
{
if(ticketNum>0)
{
System.out.println(Thread.currentThread().getName()+"卖票:"+ticketNum--);
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);//卖票窗口1
Thread t2 = new Thread(t);//卖票窗口2
Thread t3 = new Thread(t);//卖票窗口3
Thread t4 = new Thread(t);//卖票窗口4
t1.start();
t2.start();
t3.start();
t4.start();
}
}
在上面的程序中,会出现安全问题,因为一个线程中有多条语句在操作共享数据,在执行过程中,
一个线程执行到一半,另一个线程就参与进来了,就会造成共享数据出现问题
比如,修改上面的程序,
if(ticketNum>0)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
System.out.println(Thread.currentThread().getName()+"卖票:"+ticketNum--);
}
这时卖票出现了负数的票数,是因为一个线程在判断完票的总数时,就失去了执行权,另外一个线程拿到了执行权。
解决办法,让一个线程执行完所有的代码后,其他线程才能参与进来
java提供了解决的办法,同步
这时再修改上面的程序:
class Ticket implements Runnable//车票的实体类,实现Runnable接口
{
private int ticketNum = 100;//总的票数
Object o = new Object();
public void run()
{
while(true)
{
synchronized(o)
{
if(ticketNum>0)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
System.out.println(Thread.currentThread().getName()+"卖票:"+ticketNum--);
}
}
}
}
}
在操作共享数据的语句放在同步代码块中
5、同步函数:在函数上加synchronized关键字,并且同步函数所持的锁是this
6、静态同步函数,所持的锁是所在类的字节码文件对象,也就是类名.class。因为静态可以直接被类名调用。
7、死锁:同步中嵌套同步,且具有的锁是不一样的。
代码示例:
class MyThread implements Runnable
{
private boolean flag;
MyThread(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
synchronized(MyLock.locka)
{
System.out.println("if locka");
synchronized(MyLock.lockb)
{
System.out.println("if lockb");
}
}
}
else
{
synchronized(MyLock.lockb)
{
System.out.println("else lockb");
synchronized(MyLock.locka)
{
System.out.println("else locka");
}
}
}
}
}
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Thread t1 = new Thread(new MyThread(true));
Thread t2 = new Thread(new MyThread(false));
t1.start();
t2.start();
}
}
8、等待唤醒机制:当多个线程操作同一个资源,但是操作的动作不一样,,线程之间就需要通讯。
wait():线程等待
notify():唤醒线程
notifyAll():唤醒所有线程
这些方法都定义在Object类中,原因是在同一个锁的等待线程,可以被同一个锁的notify唤醒,也就是说等待很唤醒要在同一个锁中
示例:生产者和消费者操作都是统一商品,但是两者动作不同。
class Resources//商品资源
{
private String name;//商品的名称
private int num = 1;//定义计数器,作为商品编号
private boolean flag = false;//定义标记
public synchronized void set(String name)//生产商品的方法
{
while (flag)//判断标记,如果为真,执行生产的线程就等待
{
try
{
wait();
}
catch (InterruptedException e)
{
}
}
this.name = name+"---"+num++;
System.out.println(Thread.currentThread().getName()+"++生产者+++"+name);
flag = true;//改变标记为真
notifyAll();//同时唤醒其他等待的线程
}
public synchronized void out()//消费商品的方法
{
while (!flag)
{
try
{
wait();
}
catch (InterruptedException e)
{
}
}
System.out.println(Thread.currentThread().getName()+"++消费者+++++++"+name);
flag = false;
notifyAll();
}
}
class Producer implements Runnable//生产者
{
Resources res;
Producer(Resources res)
{
this.res = res;
}
public void run()
{
while (true)
{
res.set("商品");
}
}
}
class Comnsumer implements Runnable//消费者
{
Resources res;
Comnsumer(Resources res)
{
this.res = res;
}
public void run()
{
while (true)
{
res.out();
}
}
}
class ProducerComnsumerDemo
{
public static void main(String[] args)
{
Resources res = new Resources();
Producer pro = new Producer(res);
Comnsumer com = new Comnsumer(res);
new Thread(pro).start();
new Thread(com).start();
new Thread(pro).start();
new Thread(com).start();
}
}
9、JDK5.0升级后,出现了新的工具包,使线程通信更加方便
在Condition接口中,有如下方法摘要
这时候上面的例子就可以修改为如下形式:
import java.util.concurrent.locks.*;
class Resources
{
private String name;
private int num = 1;
private boolean flag = false;
private final Lock lock = new ReentrantLock();
private final Condition pro_condition = lock.newCondition();
private final Condition com_condition = lock.newCondition();
public void set(String name)throws InterruptedException
{
lock.lock();
try
{
while (flag)
pro_condition.await();
this.name = name+"---"+num++;
System.out.println(Thread.currentThread().getName()+"++生产者+++"+name);
flag = true;
com_condition.signal();
}
finally
{
lock.unlock();
}
}
public void out()throws InterruptedException
{
lock.lock();
try
{
while (!flag)
com_condition.await();
System.out.println(Thread.currentThread().getName()+"++消费者+++++++"+name);
flag = false;
pro_condition.signal();
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable
{
Resources res;
Producer(Resources res)
{
this.res = res;
}
public void run()
{
while (true)
{
try
{
res.set("商品");
}
catch (InterruptedException e)
{
}
}
}
}
class Comnsumer implements Runnable
{
Resources res;
Comnsumer(Resources res)
{
this.res = res;
}
public void run()
{
while (true)
{
try
{
res.out();
}
catch (InterruptedException e)
{
}
}
}
}
10、停止线程:
分析:使用多线程,运行的代码一般是循环结构。
方法:控制循环结构,让run方法结束,线程就就可以停止。
代码示例:
class MyThread implements Runnable
{
private boolean flag = true;//定义判断标志
private int num = 1;
public void run()
{
while (flag)
{
System.out.println(Thread.currentThread().getName()+"----run"+num++);
}
}
public void changeFlag()//改变标志的方法
{
flag = false;
}
}
class StopThread
{
public static void main(String[] args)
{
MyThread mt = new MyThread();
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
t1.start();
t2.start();
int x = 0;
while (true)
{
if (x++ == 60)
{
mt.changeFlag();//调用改变标志的方法,使run方法结束
break;//使主线程停止
}
System.out.println(Thread.currentThread().getName()+"-----run"+x);
}
}
}
但是当线程处于冻结状态时,线程就读取不到标记,线程就不能停止。这时就需要将标记清除,让线程继续运行。
Thread类中提供了如下方法,可以解决这个问题
将上面的程序修改为如下:
class MyThread implements Runnable
{
private boolean flag = true;//定义判断标志
private int num = 1;
public synchronized void run()
{
while (flag)
{
try
{
wait();
}
catch (InterruptedException e)
{
System.out.println(Thread.currentThread().getName()+"抛出异常");
}
System.out.println(Thread.currentThread().getName()+"----run"+num++);
}
}
public void changeFlag()//改变标志的方法
{
flag = false;
}
}
class StopThread
{
public static void main(String[] args)
{
MyThread mt = new MyThread();
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
t1.start();
t2.start();
int x = 0;
while (true)
{
if (x++ == 60)
{
mt.changeFlag();//调用改变标志的方法,使run方法结束
t1.interrupt();
t2.interrupt();
break;//使主线程停止
}
System.out.println(Thread.currentThread().getName()+"-----run"+x);
}
}
}
11、守护线程:当线程设置为守护线程时,当正在运行的线程都是守护线程时,程序就结束。
比如上面的程序中,如果将t1和t2标记为守护线程,当主线程结束时,t1和t2不同判断标记,线程也可以停止。
12、join方法:当A线程执行到了B线程的join()方法时,A就会等待。等B线程都执行完,A才会执行。
13、线程优先级和yield
setPriority:设置线程优先级
优先级为1-10