一、基本概念
一个应用程序就是一个进程,操作系统上可以有多个进程,一个进程中可以有多个线程。
一个CPU一个时刻只能运行一个线程,当有多个CPU时可以同时并行运行多个线程,但是线程数大于CPU数时,会有线程处于阻塞状态。
二、线程的生命周期
状态:新建、就绪、运行、阻塞、死亡。
阻塞:线程没有获取CPU资源,不能执行。
三、控制线程状态
join()方法:在thread1中执行thread2_instance.join()方法时,thread1线程阻塞,thread2线程进入运行状态;只有当thread2结束后thread1才会运行。
sleep()方法:thread1中调用Thread.sleep()方法时,thread1线程进入阻塞状态,不释放锁。
yield()方法:thread1中调用Thread.yield()方法时,thread1进入就绪状态。
wait()、noitfy()、notifyAll()方法
四、线程同步
同步问题:当多个线程并发修改某个对象时,就会出现同步问题。
控制同步:
线程的目的是操作对象,那么在线程类中把“目标对象作为成员变量”,然后“使用代码来操作、修改对象”。
使用锁来控制同步,当某线程获取锁之后,只有执行完同步代码(中间没调用wait()方法或没有被异常中断)才释放锁,其它线程才会获取锁来操作锁对象。
1、同步代码块
把操作修改对象的代码块变成同步代码块,目标对象作为锁对象。
2、同步方法
把操作修改对象的代码块变到对象类的实例方法中,把实例方法变成同步方法。通过对象调用此方法修改自己时,自己就是锁对象。
3、使用lock对象
把可能出现同步问题的代码块上锁,执行完后解锁。能保证
package grammar;
import java.util.concurrent.locks.ReentrantLock;
//被共同访问、修改的对象
class Account {
public int num;
//锁对象
private final ReentrantLock lock = new ReentrantLock();
public Account(int num) {
this.num = num;
}
//调用此方法的对象就是“上锁对象”,线程获得该对象才能调用此方法
// public synchronized void draw(int drawAmout){
public void draw(int drawAmout){
//加锁,使用Lock对象作为锁对象
lock.lock();
if (num >= drawAmout) {
System.out.println("开始取钱");
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
num = num - drawAmout;
System.out.println(num);
System.out.println("结束取钱");
//释放锁
lock.unlock();
}
}
}
// 修改对象的线程
class DrawThread extends Thread {
// 被共访修改的对象
private Account account;
private int drawAmout;
public DrawThread(Account account, int drawAmount) {
this.account = account;
this.drawAmout = drawAmount;
}
// 修改方法
public void run() {
//account调用同步方法,则account是上锁对象
account.draw(100);
/**同步代码块
//把共访修改对象作为上锁对象,只有线程获得该对象才执行“同步代码块”
synchronized (account) {
if (account.num >= drawAmout) {
System.out.println("开始取钱");
try {
// 强制让线程阻塞
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.num = account.num - drawAmout;
System.out.println(account.num);
System.out.println("结束取钱");
}
}
*/
}
}
public class MyThread {
public static void main(String[] args) {
Account account = new Account(100);
DrawThread thread1 = new DrawThread(account, 100);
DrawThread thread2 = new DrawThread(account, 100);
thread1.start();
thread2.start();
try {
// main线程阻塞,先不打印
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程");
System.out.println(account.num);
System.out.println("主线程");
}
}
五、线程间通信
借助Object的三个方法:wait()、notify()、notifyAll(),只能在同步方法(直接调用)和同步代码块(锁对象调用)中调用。
wait()方法:在thread1中使用锁对象.wait()方法,thread1进入阻塞状态,释放锁。被notify()唤醒or超时后依然处于阻塞态,只有再次获取锁后才处于就绪态。而sleep()的线程醒后就是就绪态。
notify()方法:在thread1中使用锁对象.notify()方法通知被wait()的线程后,thread1先执行完,再释放锁,其它需要获得此锁的线程才可能运行。若不使用notify()方法通知被wait()的线程,即使thread1执行完了释放锁,被wait()了的线程(超时后醒来可去竞争锁对象)任然不能和其它线程进行竞争,从而获得执行机会。