1、线程的五个状态
新建(NEW): 新创建了一个线程对象
可运行(RUNNABLE): 调用了start方法,线程就处于可执行状态。
运行(RUNNING): 可执行的线程对象获得cpu的时间片,被cpu调度执行。
阻塞(BLOCKED): 线程由于某种原因放弃了cpu的使用权,暂时停止运行。
死亡(DEAD): 线程运行完毕或者因为异常退出了run方法,停止了运行。
注意: 阻塞状态不能变为可执行状态,只能恢复可执行状态重新等待cpu调配
2、线程的常用方法
1、yeild方法(礼让)
public class MethodVip implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"在运行。。。");
}
public static void main(String[] args) {
Method method = new Method();
MethodVip methodVip = new MethodVip();
new Thread(methodVip, "佩奇").start();
new Thread(method,"乔治").start();
}
}
class Method implements Runnable{
@Override
public void run() {
Thread.yield();
System.out.println(Thread.currentThread().getName()+"在运行。。。");
}
}
分别让两个不同的类实现Runable接口,然后重写run方法,在其中一个run方法内使用yeild方法,然后调用两个线程的start方法,开启两个线程。运行多次发现使用yeild方法的线程一般都会后执行,但也有先执行的时候,由此可见礼让并不一定成功,cpu调度器可以不理会线程的礼让请求。
2、join方法(插队)
public class MethodVip implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"在运行。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
MethodVip methodVip = new MethodVip();
Thread p1 = new Thread(methodVip, "佩奇");
p1.start();
p1.join();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"在运行。。。");
}
}
}
使用join方法的线程会使主线程阻塞,自己执行完毕,主线程才能执行。
3、sleep方法(睡眠)和wait方法
sleep方法会让线程休眠,wait方法会让线程等待。区别是sleep方法不会释放锁,而wait方法会释放锁,wait是Object类的方法,sleep是Thead类的方法。
3、线程锁
1、synchronized
public class SynchronizeTest {
public static void main(String[] args) {
Bank bank = new Bank("建设银行", 100);
Person boy = new Person(bank, 50, "boy");
Person girl = new Person(bank, 80, "girl");
boy.start();
girl.start();
}
}
class Bank {
private String name;
public int money;
public Bank(String name,int money) {
this.name = name;
this.money = money;
}
}
class Person extends Thread{
Bank bank;
private int myMonmey;
private int takeMoney;
public Person(Bank bank,int takeMoney,String name){
super(name);
this.bank=bank;
this.takeMoney=takeMoney;
}
@Override
public void run() {
synchronized (bank){
if (bank.money-takeMoney<=0){
System.out.println(Thread.currentThread().getName()+"的账户没钱了");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myMonmey=myMonmey+takeMoney;
System.out.println(this.getName()+"取了"+myMonmey);
bank.money=bank.money-takeMoney;
System.out.println("银行的钱还剩"+bank.money);
}
}
}
这是模拟两个人同时在银行里面取钱的操作,由于有sleep模拟了延时,所以会出现两个人同时进入银行取钱,看到100块钱,可以对这100块钱进行操作,操作完后退出,可能出现负值。理论上资源只有一份,这样会默认资源有两份。因此我们必须对资源上锁。synchronized默认锁的是this对象,我们使用同步代码块,锁住bank对象,这样银行里的钱就只有等一个人操作完之后,另一个人才能操作。
2、lock
import java.util.concurrent.locks.ReentrantLock;
public class LockTest1{
public static void main(String[] args) {
Lock2 lock2 = new Lock2();
new Thread(lock2,"黄牛").start();
new Thread(lock2,"学生").start();
new Thread(lock2,"老师").start();
}
}
class Lock2 implements Runnable{
private int ticket=10;
boolean flag = true;
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(ticket>2){
try{
lock.lock();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买到了第张" + ticket--+"票");
}finally {
lock.unlock();
}
}
}
}
Lock是显示锁,需要用lock方法上锁再用unlock方法解锁,synchronized 锁是隐式锁,不需要声明。