1. 线程的生命周期
创建线程,调用start()方法,线程进入到就绪状态,获取cpu资源进入到运行状态。线程运行过程中会出现:
- 通过yield()方法,线程放弃cpu资源进入到就绪状态。
- 通过sleep(),join()方法线程进入阻塞状态,sleep(),join()结束后,进入到就绪状态。
- 线程运行正常结束,或者异常退出 进入死亡状态。
2. 线程安全问题
当多线程并发访问临界资源时,如果破坏原子操作,就可能会造成数据不一致。
临界资源:共享资源,一次只能给一个线程使用,才能保证其准确性。
原子操作:不可分割的多步操作,被视为一个原子操作,其顺序步骤不能被打乱或缺少。
线程安全问题是由全局变量和静态变量引起的,如果每个线程只是对全局变量和静态变量进行读操作,是不会发生线程安全问题。如果由多个线程对其进行写操作,则要考虑同步操作,否则会有线程安全问题。
例如:定义一个临界资源 int c = 100;
创建两个线程1 和 2 消费临界资源c .
正确现象: 1到100 每个数字都会打印一次。
执行之后会看到有些数字会打印多次,这就是线程安全问题。
public class OneThread {
public static void main(String[] args) throws InterruptedException {
TwoRunnable twoRunnable = new TwoRunnable();
Thread thread = new Thread(twoRunnable,"1");
Thread thread2 = new Thread(twoRunnable,"2");
thread.start();
thread2.start();
}
}
class TwoRunnable implements Runnable{
private int c = 100;
@Override
public void run() {
while (true){
if(c>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "第 "+Thread.currentThread().getName() + "个线程" + c--);
}
}
}
}
3. 锁
针对上面的线程安全问题,可以通过加锁来解决。
1. 同步代码块
synchronized 加 this (锁对象),这个this 就是锁对象,锁对象可以是任意一个对象。
synchronized (this){
}
public class OneThread {
public static void main(String[] args) throws InterruptedException {
TwoRunnable twoRunnable = new TwoRunnable();
Thread thread = new Thread(twoRunnable,"1");
Thread thread2 = new Thread(twoRunnable,"2");
thread.start();
thread2.start();
}
}
class TwoRunnable implements Runnable{
private int c = 100;
@Override
public void run() {
while (true){
synchronized (this){
if(c>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "第 "+Thread.currentThread().getName() + "个线程" + c--);
}
}
}
}
}
2. 同步方法
使用synchronized 修饰的方法 叫同步方法,在一个线程执行该方法的时候,能保证其他线程不被打扰。
synchronized 返回值 方法名(形参列表){
}
class TwoRunnable implements Runnable{
private int c = 100;
@Override
public void run() {
while (true){
test() ;
}
}
public synchronized void test(){
if(c>0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "第 "+Thread.currentThread().getName() + "个线程" + c--);
}
}
}
synchronized 在 非静态方法中锁对象是this , 在静态方法中锁对象是所在类的字节码对象(类名.class文件)
Lock
lock() //加锁
unlock() // 解锁
示例如下: 创建一个Sum类,包含两个变量, c d, 传入变量 i, 对 c d 赋值。 创建两个线程,调用sum.add()方法,正确的结果是 c d 的值都相同。如果不加锁,那么c d 的值可能会不同。
public class OneThread {
public static void main(String[] args) throws InterruptedException {
Sum sum = new Sum();
Runnable runnable1 = new Runnable() {
@Override
public void run() {
sum.add(1);
}
};
Runnable runnable2 = new Runnable() {
@Override
public void run() {
sum.add(0);
}
};
Thread t1 = new Thread(runnable1);
Thread t2 = new Thread(runnable2);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(sum.getC());
System.out.println(sum.getD());
}
}
class Sum {
private Lock lock = new ReentrantLock();
private int c = 3;
private int d = 3;
public void add(int i ){
lock.lock();//加锁
c = i ;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
d = i;
lock.unlock();//解锁
}
public int getC() {
return c;
}
public int getD() {
return d;
}
}