一、线程的5种状态:
新建——就绪——运行——阻塞——死亡
创建线程的两种方法:一种是继承Thread类,另一种是实现Runnable接口。
二、代码实例
用常见的卖票程序实现多线程为例:
第一种继承Thread类的代码:
class TicketDemo extends Thread{
public static int tick=100;
private String name;
public TicketDemo(String name) {
super(name);
}
public void run(){
while(tick>0){
System.out.println(name+"......sale...."+tick--);
}
}
public static void main(String[] args) {
TicketDemo t1 = new TicketDemo("one");
TicketDemo t2 = new TicketDemo("two");
TicketDemo t3 = new TicketDemo("three");
TicketDemo t4 = new TicketDemo("four");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
第二种实现Runnable接口的代码:
class TicketDemo implements Runnable{
private int tick = 100;
public void run(){
while(tick>0){
System.out.println(Thread.currentThread().getName()+"......sale...."+tick--);
}
}
public static void main(String[] args) {
TicketDemo t = new TicketDemo();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
三、线程同步 (安全)
在这里这两段代码的运行结果颇有不同,经过测试,在这里做一下解释。
代码一:结果运行正常
public void run(){
while(tick>0){
System.out.println("......sale...."+tick--);
}
}
代码二:结果运行错误
public void run(){
while(tick>0){
System.out.println("......sale...."+tick);
tick--;
}
}
当多线程执行System.out.println("......sale...."+tick--)时,因为这是一条语句,执行完后才有可能切换至另一线程。
而代码二内,JVM执行System.out.println("......sale...."+tick)这条语句后,有可能因为切换到另一线程而未能执行tick--,这样导致有可能几个线程内的某值相同,如线程t1、t2、t3、t4的值有可能同时=100。
这就涉及到多线程的安全问题了。
要用代码二这种方式,就需要用到
Synchronized(对象) {……}
完整代码:
class TicketDemo implements Runnable
{
private int tick = 100;
Object obj = new Object();
public void run(){
synchronized(obj){//这里的obj也称之为锁或锁旗标
while(tick>0){
System.out.println(Thread.currentThread().getName()+"......sale...."+tick);
tick--;
}
}
}
public static void main(String[] args)
{
TicketDemo t = new TicketDemo();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
总结:当多线程对共享对象进行操作时,就需要考虑多线程的安全问题。解决方法经常会用到synchronized关键字。Synchronized关键字用来标志被同步使用的资源。这里的资源既可以是数据,也可以是方法,甚至是一段代码。凡是被synchronized修饰的资源,系统都会为它分配一个管程,这样就能保证在某一时间内,只有一个线程对象在享有这一资源。
synchronized的使用形式有两种,一种是保护整个方法:
访问类型 synchronized返回值方法名(【参数表】){……}
另外一种是保护某个指定的对象以及随后的代码块(见上述代码中):
synchronized(对象名) {……}
同步的前提:
1、必须有两个或两个以上的线程
2、多个线程必须使用同一个锁
同步的好处:
解决了线程的安全问题
同步的弊端:
每次执行都会对锁进行判断,较为消耗资源。
四、线程互斥
要实现线程互斥,需要以下几个步骤:
设置一个各个线程共享的信号量,值为true或者false。
线程需要访问共享资源前,先检测信号量的值。如果不可用,则调用wait()转入等待状态。
如果可用,则改变信号量的状态,不让其他线程进入。
访问完共享资源后,再修改信号量的状态,允许其他线程进入。
调用notify()或notifyAll(),唤醒其他等待的线程。