多线程
1,创建一个任务
实现Runnable接口,并重写run()方法
class MyRunnable implements Runnable{
public void run() {
System.out.println("创建了一个任务");
}
}
2,创建线程
<1>创建一个类,继承Thread类,并重写run()方法,创建此类对象。
<2>实现Runnable接口,并重写run()方法,利用Thread类的构造方法,传入此类。
public class T1 {
public static void main(String[] args) {
Runnable a = new MyRunnable();//创建任务
Thread u1 = new Thread(a); //利用Thread类的构造方法创建线程
MyThread b = new MyThread(); //利用自己写的线程类创建了一个线程
}
}
class MyRunnable implements Runnable{
public void run() {
System.out.println("创建了一个任务类");
}
}
class MyThread extends Thread{
public void run() {
System.out.println("创建了一个线程类");
}
}
3,启动线程
调用线程对象的start()方法
public class T1 {
public static void main(String[] args) {
Runnable a = new MyRunnable();//创建任务
Thread u1 = new Thread(a); //创建线程
u1.start(); //启动线程
}
}
4,同步锁
在线程并行时期,哪个线程获取了cpu的执行权都是不可预测的。在某种情况下这种不可预测的特性将会造成线程并行操作相同的数据时,出现错误。
-
例:
- 用代码实现,窗口一、窗口二、两个窗口同时进行买票(总共十张票)
public class TicketDemo {
public static void main(String[] args) {
M r = new M();//创建任务
Thread t1 = new Thread(r,"窗口一");
Thread t2 = new Thread(r,"窗口二");
t1.start();
t2.start();
}
}
class M implements Runnable{
private int ticket=10;// 剩余票数
@Override
public void run() {
while(true){
if(ticket<=0){
return;
}
ticket--;
System.out.println(Thread.currentThread().getName()+
"售出第" + (10-ticket)+"票");
//为体现出交替买票的效果,卖出一张票之后,线程睡眠10毫秒,让另一个窗口抢到CPU执行权
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
执行结果为:
下面的代码使用了同步锁
public class TicketDemo {
public static void main(String[] args) {
M r = new M();//创建任务
Thread t1 = new Thread(r,"窗口一");
Thread t2 = new Thread(r,"窗口二");
t1.start();
t2.start();
}
}
class M implements Runnable{
private int ticket=10; // 剩余票数
private Lock lock = new Lock();//创建唯一的锁
@Override
public void run() {
while(true){
//同步代码块
synchronized (lock) {
if(ticket<=0){
return;
}
ticket--;
System.out.println(Thread.currentThread().getName()+
"售出第" + (10-ticket)+"票");
}//在此地方释放唯一锁
//为体现出交替买票的效果,卖出一张票之后,线程睡眠10毫秒,让另一个窗口抢到CPU执行权
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Lock{
}
运行结果为:
在使用同步锁时一定要注意,锁是否是唯一的!
5,同步方法
1,同步方法的监视器是this
2,在同一个对象中,线程在调用同步方法的时候,其他的同步方法无法执行(因为this没有被释放)
6,线程状态
<1>创建
(1)当new一个Thread时,线程就被创建了出来。
<2>就绪
(1)当线程调用start()方法后,就进入了就绪状态,随时准备抢夺cpu执行权
<3>阻塞
(1)在,当前线程调用Thread.sleep(时间t)方法时,在时间t内,调用此方法所在的线程一直处于阻塞状态,直到经过时间t后重新进入就绪状态
(2)当线程要执行同步代码块而没有Lock的时候,进入阻塞状态,直到拿到Lock才进入运行状态
(3)在,当前线程中的对象执行wait()方法时,表示进入阻塞状态,只有当另一个线程内的此对象执行notify()方法后,才被唤醒,重新进入就绪态。应当注意的是,此两种方法是属于Object类内的。而不是Thread类内。此外,这两句代码执行时,不允许可能会出现其他的线程进行同步执行,所以这两句代码一定要在同步代码块中执行。
当多个线程中的此对象都在等待时间,可以使用notifyAll()方法,使之全部唤醒。
下面是一个使用wait()方法和notify()方法的一个小例子。
/*
* wait()方法会释放当前锁(wait()方法表示当前对象资源没有准备好)
* notify()(通知当前对象资源已经准备好了)
*
* 题目:
* A : 1000元
* B : 1000元
*
* A -> 转账给B 100元
* B -> 转账给A 100元
*
* 最后两人仍旧剩余1000元
* 要求:
* 必须是A先转账给B,然后B再转账给A
* 分别开启两个线程运行这两个任务。
*
* */
public class T7 {
public static void main(String[] args) {
Rank rankA = new Rank("A",1000);
Rank rankB = new Rank("B",1000);
Ra a = new Ra(rankA,rankB);
Rb b = new Rb(rankA,rankB);
Thread t1 = new Thread(a);
Thread t2 = new Thread(b);
t1.start();
t2.start();
}
}
//任务a
class Ra implements Runnable{
Rank a = null;
Rank b = null;
public Ra(Rank a, Rank b) {
super();
this.a = a;
this.b = b;
}
@Override
public void run() {
System.out.println("a余额:"+a.getMoney());
System.out.println("b余额:"+b.getMoney());
synchronized (a) {
a.setMoney(a.getMoney()-100);
b.setMoney(b.getMoney()+100);
System.out.println();
System.out.println("A向B转账一百元");
System.out.println("a余额:"+a.getMoney());
System.out.println("b余额:"+b.getMoney());
}
//若a已经给了b一百元,则通知线程t2我已经给了b一百元了,可以往下进行了
if(b.getMoney()>1000) {
synchronized(b) {
b.notify();
}
}
}
}
//任务b
class Rb implements Runnable{
Rank a = null;
Rank b = null;
public Rb(Rank a, Rank b) {
super();
this.a = a;
this.b = b;
}
@Override
public void run() {
//如果a没有向b转账一百元,则线程就进入阻塞状态
if(b.getMoney()<=1000) {
synchronized (b) {
try {
b.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
synchronized (a) {
b.setMoney(b.getMoney()-100);
a.setMoney(a.getMoney()+100);
System.out.println();
System.out.println("B向A转账一百元");
System.out.println("a余额:"+a.getMoney());
System.out.println("b余额:"+b.getMoney());
}
}
}
//一个银行账户类
class Rank{
private String id ;
private float money;
public Rank(String id, float money) {
super();
this.id = id;
this.money = money;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public float getMoney() {
return money;
}
public void setMoney(float money) {
this.money = money;
}
}
(5)当执行join()方法时,会让当前调用此方法的线程进入运行状态,若是没有指定运行时间,则在当前线程运行完(进入死亡状态后)其他线程才能够继续抢夺cpu执行权。
(6)同样,既然有强制插入线程的方法,自然也有强制礼让线程的方法。yield()方法,会使得运行此方法的当前线程进入就绪状态,但值得注意的是,礼让出来的cpu执行权只会被那些优先级与当前线程相同,或者比当前线程更加高的线程抢夺到。
<4>运行
<5>死亡
一些有趣的小问题
1,如果在同步代码块中执行yield()方法会造成什么结果?
2,如果一个线程没有wait()的话,notify会造成什么结果?