--------------------ASP.Net+Android+IOS开发、.Net培训、期待与您交流! --------------------
1.实现Runnable接口
1.步骤
1.创建类(A)实现接口Runnable,复写run()方法
2.创建Thread对象,然后把类A的对象作为参数传给Thread的构造函数,这是把其关联起来。
3.通过创建的线程对象,然后开启线程。
解答:复写run()方法,里面存的是线程要运行的代码
因为Runnble接口中只有一个run()方法,所以没有办法启动线程,所以还得使用Thread类来启动线程,所以要把Runnable的子类的对象作为参数传给Thread类构造函数。
2.售票例子(Runnable接口)
public class Tick implements Runnable {
private int tick = 10;
public void run(){
while (tick > 0) {
System.out.println(Thread.currentThread().getName() + "卖了第"
+ (tick--) + "张票");
}
}
}
结果:
Thread-0卖了第10张票
Thread-1卖了第10张票
Thread-0卖了第9张票
Thread-1卖了第8张票
Thread-0卖了第7张票
Thread-1卖了第6张票
Thread-0卖了第5张票
Thread-0卖了第4张票
Thread-0卖了第3张票
Thread-0卖了第2张票
Thread-0卖了第1张票
Thread-1卖了第0张票
从结果上可以看出,这是我们想要的那种结果,多个窗口共同买共有的票。
3.Runnable接口和Thread类的区别与联系
1.Runnable接口可以避免单继承的限制,要是继承Thread类的话,那么就不能继承其他的类了,因为只能单继承,如果实现了接口Runnale后,还可以继承其他的类,或是是实现其他的接口
2.Runnable实现了资源的共享,例如(售票程序的票)
3.Runnable实增强了程序的健壮性,代码能够被多个程序共享,实现了数据 与代码是独立的。
4.实现Runnable接口的线程的运行代码存在实现Runnable接口子类的run()方法中,继承Thread类的线程的代码存在Thread子类的run()方法中。
所以在以后的开发中,使用Runnble接口比较好,更多。
2.线程的安全问题
1.原因
当多条语句执行多个线程共享的资源,执行到一部分后,执行权被抢夺了,导致共享资源的不正正常修改,所以就产生了线程的安全问题。
例如:售票小例子
public class Ticket implements Runnable {
private int tick = 10;
public void run() {
while (true) {
try {
if (tick > 0) {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+ "卖了第"
+ (tick) + "张票");
tick--;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Text {
public static void main(String[] agrs) {
Ticket tt=new Ticket();
new Thread(tt).start();
new Thread(tt).start();
}
}
结果:
Thread-1卖了第10张票
Thread-0卖了第10张票
Thread-1卖了第8张票
Thread-0卖了第8张票
Thread-0卖了第6张票
Thread-1卖了第5张票
Thread-1卖了第4张票
Thread-0卖了第3张票
Thread-0卖了第2张票
Thread-1卖了第2张票
Thread-0卖了第0张票
结果出现了:产生了重复的票,并且出现了0票,那么这是不正常的现象的。
2.同步代码块---解决办法
1.概述
利用的是关键子(synchronized)来处理线程同步问题,保证线程安全,锁就好比门上的锁一下,执行的顺序是:首先是判断锁是否是开着的,若是开着的,那么就可以进去(执行同步代码)然后把锁锁上,执行完后,把锁释放(把锁开开),判断锁是锁着的,那么就在外面等着,直到里面的人把锁开开,出来。(这样保证的是同步代码在某一段时间只有一个线程在执行。)
2.格式
synchronized(对象){执行共享资源的代码}
3.同步规则
必须是多个线程执行(至少两个)才可以产生同步,必须是多个线程使用的是同一个锁,
优点:解决了多线程的安全问题
弊端:多线程需要判断,那么就会消耗时间,消耗资源
public class Ticket implements Runnable {
private int tick = 10;
public void run() {
while (true) {
synchronized (this) {
try {
if (tick > 0) {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()
+ "卖了第" + (tick) + "张票");
tick--;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
结果:
Thread-0卖了第10张票
Thread-0卖了第9张票
Thread-0卖了第8张票
Thread-1卖了第7张票
Thread-1卖了第6张票
Thread-1卖了第5张票
Thread-1卖了第4张票
Thread-1卖了第3张票
Thread-1卖了第2张票
Thread-1卖了第1张票
这样结果就和我们正常想要的是一样的,没有异常现象
3.同步方法---解决办法
1.概述
因为方法和代码块都是用来封装代码的,那么代码块可以使用锁来解决同步问题,那么函数也可以使用锁,使方法也可以操作同步问题。
2.格式
public synchronized 方法返回类型 方法名(参数){方法体}
3.使用
怎眼确定锁的位置,寿面判断那些是线程运行的代码,那些是共享资源,那些同步代码是执行的是同步资源,那么就把那些操作同步资源的语句使用锁来进行括起来。
例如:客户向银行存钱,每次存三次,每次存100,分两个地方存户存
思路:共享资源:总价钱,银行,有两个线程(模拟两个地方存)
**
* 银行类
*/
public class Bank {
private int sum;// 表示的是当前账户的总额
public synchronized void add(int mon) {// 存方法,加锁的话,那么里面的语句在一段时间内必须是一个线程在执行
sum += mon;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sum=" + sum);
}
}
public class Cus implements Runnable {
private Bank bank=new Bank();
public void run(){//线程执行的代码(线程)
for(int i=0;i<3;i++){
bank.add(100);
}
}
}
public class Text {
public static void main(String[] agrs) {
Cus tt=new Cus();//客户
new Thread(tt).start();//开始存
new Thread(tt).start();//开始存
}
}
结果:
sum=100
sum=200
sum=300
sum=400
sum=500
sum=600
4.同步代码块和同步函数的选择
假如连续的代码是同步代码,并且都是执行的都是共享资源,那么就可以把其抽取成函数,并且定义为同步函数
假如一个函数中是全是同步代码,并且执行的全是共享资源,那么就把其定义为同步函数
5.扩展
回顾:一共分为四种代码块
普通代码块:就是定义在方法中的代码块。
构造块:定义在类中的代码块,优先于构造方法,重复调用。
静态块:使用static关键字声明的,只执行一次,优先于构造块。
同步代码块:使用synchronized关键字声明的代码块,称为同步代码块。格式:synchronized(同步对象){}
4.锁对象的确定
1.同步函数的锁是this
验证思路:还是使用的卖票程序,使用两个线程,他们执行的代码不同,一个是同步代码块(锁的对象是this synchronized(this){}),另一个线程执行的同步函数,如果结果没有异常,那么他们就完全符合线程同步的规则,
多个线程执行的是同步代码,并且他们的是锁是同一个锁,因为同步代码块的锁对象是this,那么此时同步函数锁的对象也是this
/*售票机*/
public class Ticket implements Runnable {
private int tick = 100;
public boolean flag = true;
public void run() {
if (flag) {
while (true) {
synchronized (this) {
try {
if (tick > 0) {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()
+ "卖了第" + (tick--) + "张票");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} else
while (true)
show();
}
public synchronized void show() {// 同步函数
try {
if (tick > 0) {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+ "卖了第"
+ (tick) + "张票");
tick--;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Text {
public static void main(String[] agrs) {
Ticket tt=new Ticket();
new Thread(tt).start();
/*这里是主线程睡眠一下,让线程1先运行,防止直接改变标志位*/
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
tt.flag=false;//更改标志位
new Thread(tt).start();//开始存
}
}
部分结果:
Thread-0卖了第100张票
Thread-0卖了第99张票
Thread-0卖了第98张票
Thread-0卖了第97张票
Thread-0卖了第96张票
Thread-0卖了第95张票
Thread-1卖了第94张票
Thread-1卖了第93张票
Thread-1卖了第92张票
从结果上额可以看出,结果是正常的,所以结论是正确的,同步函数的锁对象是this
2.静态同步函数锁对象
静态同步函数的锁是:该方法所在类的字节码文件对象:类名.class
验证方法:和上面的一样,把同步代码块的锁对象改为:Ticket.class
/*售票机*/
public class Ticket implements Runnable {
private static int tick = 100;
public boolean flag = true;
public void run() {
if (flag) {
while (true) {
synchronized (Ticket.class) {
try {
if (tick > 0) {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()
+ "卖了第" + (tick--) + "张票");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} else
while (true)
show();
}
public static synchronized void show() {// 同步函数
try {
if (tick > 0) {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+ "卖了第"
+ (tick) + "张票");
tick--;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:和上面的一样,正常
我们可以这样思考,静态方法是随着类的加载而加载,此时没有类的对象,但是还是要找个对象,那么就是类的字节码文件的对象。
3.死锁
1.死锁产生的原因
死锁产生的原因是:一个所中还有另外一个锁,但是这两个锁对象是不相同的,其中一个锁(A)需要另外一个锁(B),而锁(B)也需要锁(A),这样有时候她们都不会妥协,那么就会产生死锁。
2.举例死锁
class MyLock {
static Object lockA = new Object();
static Object lockB = new Object();
}
public class DeadLock implements Runnable {
public boolean flag = true;
public DeadLock(boolean f) {
this.flag = f;
}
public void run() {
if (flag) {
// while (true) {
synchronized (MyLock.lockA) {
System.out.println("ifLockA");
synchronized (MyLock.lockB) {
System.out.println("ifLockB");
}
// }
}
} else {
// while (true) {
synchronized (MyLock.lockB) {
System.out.println("elseLockB");
synchronized (MyLock.lockA) {
System.out.println("elseLockA");
}
// }
}
}
}
}
public class Text {
public static void main(String[] agrs) {
DeadLock l1 = new DeadLock(true);// 第一个线程
DeadLock l2 = new DeadLock(false);// 第二个线程
new Thread(l1).start();// 开启线程
new Thread(l2).start();// 开启线程
}
}
结果:可能会死锁
例如:
if LockA
else LockB
这就产生了死锁
1. 资源共享的时候需要进行同步操作
2. 程序中过多了同步操作会产生死锁
死锁就是程序中互相的等待。
--------------------ASP.Net+Android+IOS开发、.Net培训、期待与您交流! --------------------