1、线程的实现的方式
实现多线程的方式有两种:
1. 继承 Thread
2. 实现 Runnable
1.1 继承 Thread
public class MutiThread extend Thread{
@override
public void run(){
.....
}
}
1.2 实现 Runnable
public class MutiThread implements Runnable{
@override
public void run(){
.....
}
}
具体的调用线程:
mThread.start();
- 从构造函数
Thread(Runnable runnable)
可知不光可以传入实现了Runnable
接口的对象,还可以传入Thread
对象,这样就可以Thread
对象的run()
方法交由其他的线程进行调用。Thread.java
类中的 start() 方法是通知“线程规划器”此时线程已经准备就绪,等待调用线程对象的run()
方法。这个过程其实是让系统安排一个时间来调用Thread
中的run()
方法,也就是让线程得到运行,启动线程,具有异步执行的效果。如果调用mThread.run()
方法就不是异步执行,而是同步执行,那么此时线程对象不是交给“线程执行器”来进行处理,而是由mThread.run()
所在的线程调用run()
方法。- 线程是一个子任务,也就是说在使用子线程技术的时候,代码的运行的结果与代码执行顺序或调用顺序无关,而在同步线程中代码的执行顺序和代码的顺序是一致的。
2、线程安全问题
非线程安全主要是指多个线程对同一个对象的同一个实例变量进行操作时会出现的值被更改、值不同步的情况,进而影响程序的执行流程。
2.1 线程不共享数据
线程不共享数据即为每个线程的私有的变量,此时当然就不会发生线程安全问题。
一个简单的例子来说明这种情况:
public class CustomThread extends Thread{
int count = 0;
@Override
public void run(){
....
}
}
具体调用:
public class Run{
public static void main(){
CustomThread mCustomThreadA = new CustomThread();
CustomThread mCustomThreadB = new CustomThread();
CustomThread mCustomThreadC = new CustomThread();
mCustomThreadA.start();
mCustomThreadB.start();
mCustomThreadC.start();
}
}
很明显,此时变量 count 便属于线程不共享数据,因为每个对象都有自己内存区域,这没什么可说的,不在这浪费口舌了。
2.2 线程共享数据
同样我们通过一个例子来说明什么是线程不共享数据;
public class CustomThread extends Thread{
int count = 5;
@Override
public void run(){
count--;
sysout("thread name is " + this.currentThread().getName() + "count = "+ count);
}
}
public class Run{
public static void main(){
CustomThread mCustomThread = new CustomThread();
Thead threadA = new Thead(mCustomThread,"A");
Thead threadB = new Thead(mCustomThread,"B");
Thead threadC = new Thead(mCustomThread,"C");
Thead threadD = new Thead(mCustomThread,"D");
Thead threadE = new Thead(mCustomThread,"E");
threadA.start();
threadB.start();
threadC.start();
threadD.start();
threadE.start();
}
}
打印结果:
thread name is A,count = 3
thread name is C,count = 3
thread name is B,count = 2
thread name is D,count = 2
thread name is E,count = 0
此时,变量 count
便属于线程共享数据,因为threadA
threadB
访问的为同一个对象mCustomThread
的同一个变量(线程安全出现的条件:1.多个线程 ;2.同一个对象; 3.同一个实例变量)。显然上面的结果不是我们想要的结果,我们想要的结果是 count
依次递减。
2.3 线程安全问题的原因
就拿上面的例子中 count--
来说,在有些 JVm 中,count--
的操作分为三步:
1. 取得 count
的值
2. 计算count - 1
3. 对 count
进行赋值
在以上的三步中,如果有多个线程同时访问并进行相关变量的操作,那么一定会出现线程安全问题。
3、 线程安全问题的解决办法 – synchronized 关键字
通过在 run
方法前加入 synchronized
关键字,使多个线程在执行run
方法的时候以 排队 的方式执行。当一个线程执行 run
方法前,先判断 run
方法上有没有被上锁,如果上锁,说明现在有其他线程正在调用run
方法,必须等其他线程执行完 run
方法才可以执行run
方法。
具体代码:
public class CustomThread extends Thread{
int count = 5;
/**
* 添加 synchronized 关键字
*/
@Override
synchronized public void run(){
count--;
sysout("thread name is " + this.currentThread().getName() + "count = "+ count);
}
}
public class Run{
public static void main(){
CustomThread mCustomThread = new CustomThread();
Thead threadA = new Thead(mCustomThread,"A");
Thead threadB = new Thead(mCustomThread,"B");
Thead threadC = new Thead(mCustomThread,"C");
Thead threadD = new Thead(mCustomThread,"D");
Thead threadE = new Thead(mCustomThread,"E");
threadA.start();
threadB.start();
threadC.start();
threadD.start();
threadE.start();
}
}
打印结果:
thread name is A,count = 4
thread name is C,count = 3
thread name is B,count = 2
thread name is D,count = 1
thread name is E,count = 0
当一个线程想要执行同步方法里面的代码时,该线程首先尝试去拿这把锁,如果能拿到这把锁,那么这个线程就可以执行 ·synchronized` 里面的代码,如果拿不到这把锁,那么这个线程就会和其他没有拿到锁的线程不断的尝试去拿这把锁,直到拿到为止。