1.线程的创建实现类
在创建的过程中,我们需要了解两个类:1.Thread类 2.Runnable类
1.1 Thread类
Thread类中常用的方法:
1.2 Runnable接口
2. 线程的创建和使用
2.1 Thread类实现
代码如下:
package com_imooc.xiancheng;
class Mytest extends Thread {
//实现线程,必须重写run()方法
public void run() {
System.out.println("线程启动了");
}
}
public class xiancheng_test {
public static void main(String[] args) {
Mytest t = new Mytest();
/**
* 启动线程使用的是start()方法
* 一个线程只能启动一次
*/
t.start();
}
}
结果输入如下:
可能有的小伙伴会说,这线程和我们之前使用main的方法是一致的呀,没有什么不同的呀,其实我们此时的程序中两个线程,既:主线程和子线程,为了让我们更加清楚的明白些,我们代码修改如下:
package com_imooc.xiancheng;
class Mytest extends Thread {
//实现线程,必须重写run()方法
public void run() {
System.out.println("线程启动了");
}
}
public class xiancheng_test {
public static void main(String[] args) {
System.out.println("我是主线程1");
Mytest t = new Mytest();
/**
* 启动线程使用的是start()方法
* 一个线程只能启动一次
*/
t.start();
System.out.println("我是主线程2");
}
}
结果输出为:
自此,我们可以很清楚的看出,并不是我们顺序执行的代码,而是先主后子的执行方式;
当然,我们也可以直接用for来多运行几次,代码如下:
package com_imooc.xiancheng;
class Mytests extends Thread {
public Mytests(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+"正在run的线程:"+i);
}
}
}
public class xianchengs_test {
public static void main(String[] args) {
Mytests m1=new Mytests("a");
Mytests m2=new Mytests("b");
m1.start();
m2.start();
}
}
结果为:
可以看出上面例子的输出并不是顺序执行的,由此可见是抢占式的运行;
2.2 Runnable接口实现
首先你得明白为什么有了Thread类还需要Runnable接口:
在明白为什么需要创建Runnable之后,我们创建他的代码:
package com_imooc.xiancheng;
class test implements Runnable {
//重写run方法可以调用Thread类的静态方法currentThread()
@Override
public void run() {
int i = 0;
while (i < 10)
System.out.println(Thread.currentThread().getName() + "正在进行" + i++);
}
}
public class runnable_test {
public static void main(String[] args) {
//启动进程的时候需要调用Thread的构造方法Thread(Runnable r)
test able = new test();
Thread t = new Thread(able);
t.start();
test able1 = new test();
Thread t1 = new Thread(able1);
t1.start();
}
}
结果:
和之前的结果一样,t和t1都是执行了10次,有没有办法让我们实现两个线程一共执行10次呢??
当然有,方法如下:
package com_imooc.xiancheng;
class test implements Runnable {
int i = 0;
//重写run方法可以调用Thread类的静态方法currentThread()
@Override
public void run() {
while (i < 10)
System.out.println(Thread.currentThread().getName() + "正在进行" + i++);
}
}
public class runnable_test {
public static void main(String[] args) {
//启动进程的时候需要调用Thread的构造方法Thread(Runnable r)
test able = new test();
Thread t = new Thread(able);
t.start();
Thread t1 = new Thread(able);
t1.start();
}
}
结果如下:
在代码中,因为t和t1的开启都是由同一个test类实例而来,所以内存共享数据int i的变化,最终两个线程抢占执行;
最后查看一下线程的生命周期;
2.3 sleep方法
package com_imooc.xiancheng;
class tests implements Runnable {
//重写run方法可以调用Thread类的静态方法currentThread()
@Override
public void run() {
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() + "执行第" + i + "次");
//sleep()方法需要捕获InterruptedException异常
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class runnables_test {
public static void main(String[] args) {
//启动进程的时候需要调用Thread的构造方法Thread(Runnable r)
tests able = new tests();
Thread t = new Thread(able);
t.start();
Thread t1 = new Thread(able);
t1.start();
}
}
2.4 join方法
join方法是等待线程执行完以后再去执行下面的;
package com_imooc.xiancheng;
class join_test implements Runnable {
//重写run方法可以调用Thread类的静态方法currentThread()
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行中");
}
}
}
public class join {
public static void main(String[] args) {
//启动进程的时候需要调用Thread的构造方法Thread(Runnable r)
join_test able = new join_test();
Thread t = new Thread(able);
t.start();
try {
t.join();
//代表执行1秒以后不管执行完与否,执行下面的语句;
//t.join(1111);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println("主程序结束" + i);
}
}
}
3.线程的优先级
除了数字可以表示以外,我们也可用优先级的常量来表示:
最后,我们可以通过以下的方法来获取和设置优先级:
代码实例:
package com_imooc.xiancheng;
class youxianji_test implements Runnable {
private String name;
public youxianji_test(String name) {
this.name = name;
}
//重写run方法可以调用Thread类的静态方法currentThread()
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(name + "正在进行" + i);
}
}
}
public class youxianji {
public static void main(String[] args) {
//1.获取主线程的优先级
int main_number = Thread.currentThread().getPriority();
System.out.println("主线程的优先级是:" + main_number);
//2.设置线程的优先级
youxianji_test able = new youxianji_test("aaa");
Thread t = new Thread(able);
t.setPriority(10); //设置的具体方法
t.start();
youxianji_test able1 = new youxianji_test("bbb");
Thread t1 = new Thread(able1);
t1.setPriority(1); //设置的具体方法
t1.start();
}
}
结果如下:
值得注意的是:线性的优先级并不是越高,就越要先执行,而是和操作系统,cpu,代码执行顺序等有关的.
4.多线程的同步(synchronized)
现在列举一个银行取款的例子:
bank.java
package com_imooc.xiancheng.synchronized_test;
public class bank {
private int zhanghao;
private double money;
public bank(int zhanghao, double money) {
this.zhanghao = zhanghao;
this.money = money;
}
public int getZhanghao() {
return zhanghao;
}
public void setZhanghao(int zhanghao) {
this.zhanghao = zhanghao;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
//存款
public void cun(double m) {
double money = getMoney();
money += m;
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
setMoney(money);
System.out.println("cun存款后余额为:" + money);
}
//取款
public void qu(double m) {
double money = getMoney();
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
money = money - m;
setMoney(money);
System.out.println("qu取款后余额为:" + money);
}
@Override
public String toString() {
return
"账号:" + zhanghao +
", 余额:" + money;
}
}
运行的main.java
package com_imooc.xiancheng.synchronized_test;
class yue implements Runnable {
bank bank;
String type;
double money;
public yue(com_imooc.xiancheng.synchronized_test.bank bank, String type, double money) {
this.bank = bank;
this.type = type;
this.money = money;
}
@Override
public void run() {
if (type.equals("-")) {
bank.qu(money);
} else if (type.equals("+")) {
bank.cun(money);
}
}
}
public class tongbu {
public static void main(String[] args) {
//创建银行账户
bank b = new bank(1, 500);
//创建存取钱两个线程
yue y = new yue(b, "-", 200);
yue y1 = new yue(b, "+", 300);
Thread t = new Thread(y);
Thread t1 = new Thread(y1);
//启动线程
t1.start();
t.start();
try {
t1.join();
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(b.toString());
}
}
结果输出:
我们可以看出,运行的程序出现了脏读,所以我们需要synchronized(上锁)来保证多线程下数据的一致性;
所以我们的代码中可以直接在public中加入synchronized:
也可以用synchronized (this) {}:
5.线程的通信
此时我们模拟一个MQ的实例来说明我们的通讯问题:
queue.java
package com_imooc.xiancheng.tongxun_test;
public class queue {
private int n;
public synchronized int getN() {
System.out.println("输出:" + n);
return n;
}
public synchronized void setN(int n) {
System.out.println("存入:" + n);
this.n = n;
}
}
main.java
package com_imooc.xiancheng.tongxun_test;
class duilie implements Runnable {
queue queue;
String type;
int i = 0;
public duilie(com_imooc.xiancheng.tongxun_test.queue queue, String type) {
this.queue = queue;
this.type = type;
}
@Override
public void run() {
if (type.equals("set")) {
while (true) {
queue.setN(i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else if (type.equals("get")) {
while (true) {
queue.getN();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class tongxun {
public static void main(String[] args) {
queue q = new queue();
new Thread(new duilie(q, "set")).start();
new Thread(new duilie(q, "get")).start();
}
}
结果是:
仅仅从前几个数据,我们就可看出此MQ是有问题的,因为一次入列以后,竟然有两次的出列行为,这是万万不允许的错误,而我们要解决代码的bug,就用到了我们线程间的通信:
queue.java
package com_imooc.xiancheng.tongxun_test;
public class queue {
private int n;
boolean flag = false; //flag=true 存在,可取出; flag=false 不存在,不可取出
public synchronized int getN() {
//不存的时候在等待
if (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("输出:" + n);
flag = false; //消费完毕,队列没有数据
notifyAll(); //启动所有线程(以防止死锁)
return n;
}
public synchronized void setN(int n) {
//存在的时候等待
if (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("存入:" + n);
this.n = n;
flag = true; //生产完毕,队列有数据
notifyAll(); //启动所有线程(以防止死锁)
}
}
结果运行:
此时MQ输入输出正常;