线程二

66 阅读3分钟

1. 线程的生命周期

image.png

创建线程,调用start()方法,线程进入到就绪状态,获取cpu资源进入到运行状态。线程运行过程中会出现:

  1. 通过yield()方法,线程放弃cpu资源进入到就绪状态。
  2. 通过sleep(),join()方法线程进入阻塞状态,sleep(),join()结束后,进入到就绪状态。
  3. 线程运行正常结束,或者异常退出 进入死亡状态。

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--);
                }
            }
        

    }
}

image.png

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;
    }
}