Java-线程基础

一、线程的状态

  • New新建
  • Runnable可运行
  • Running运行
  • Blocked阻塞
  • Dead死亡

  New新建,就是创建一个线程。
  Runnable可运行,线程启动start,等待cpu调度。
  Running运行,线程正在执行,cpu正在调度。
  Blocked阻塞。由于某种原因,线程暂停,释放cpu的使用权。直到结束,重新返回就绪状态,等待cpu调用。
  Dead死亡,线程成功或不成功的退出,完成了整个生命周期。

二、线程的创建和启动方式

1、Thread继承

  继承Thread的方式,重写run方法。

 class MyThread extends Thread {     
     private int i = 0;
      @Override
      public void run() {
          for (i = 0; i < 100; i++) {
              System.out.println(Thread.currentThread().getName() + " " + i);
          }
     }
 }
public class ThreadTest {
    public static void main(String[] args) {
                Thread myThread  = new MyThread();     // 创建一个新的线程  myThread  此线程进入新建状
                myThread.start();                     // 调用start()方法使得线程进入就绪状态
    }
}

2、实现Runnable接口

  实现Runnable的接口,重写run方法。

class MyRunnable implements Runnable {
    private int i = 0;
    @Override
    public void run() {
        for (i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
                 Runnable myRunnable = new MyRunnable(); // 创建一个Runnable实现类的对象
                 Thread thread = new Thread(myRunnable); // 将myRunnable作为Thread target创建新的线程
                 thread.start();                    // 调用start()方法使得线程进入就绪状态
    }
}

  有一个问题如果有这么一个类实现Runnable接口,有个类继承Thread,那么问题来了,线程到底运行那个程序。

class MyRunnable implements Runnable {
    @Override
    public void run() {
          System.out.println("this is MyRunnable");
    }
}
class MyThread extends Thread {
    @Override
    public void run() {
          System.out.println("this is MyThread");
    }
}
public class ThreadTest {
    public static void main(String[] args) {
         Runnable myRunnable = new MyRunnable();
         Thread myThread  = new MyThread(myRunnable);
         myThread.start();
    }
}

  根据实践,得出真正运行的是MyThread中的run。因为Thread中继承的Runnable,在Thread中run中

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

  其中这个target就是Runnable,又因为子类重写了这个run,所以运行的是子类的run。

3、使用Callable和Future接口创建

  这个方式可以使用线程的返回值进行其他操作。

class MyCallable implements Callable<Integer> {
    private int i = 0;
    // 与run()方法不同的是,call()方法具有返回值
    @Override
    public Integer call() {
        int sum = 0;
        for (; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
            sum += i;
        }
        return sum;
    }
}
public class ThreadTest {
    public static void main(String[] args) throws Exception{
        Callable<Integer> myCallable = new MyCallable();    // 创建MyCallable对象
        FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask来包装MyCallable对象
        Thread thread = new Thread(ft);   //FutureTask对象作为Thread对象的target创建新的线程
        thread.start();                      //线程进入到就绪状态
                
        int sum = ft.get();            //取得新创建的新线程中的call()方法返回的结果
        System.out.println("sum = " + sum);
    }
}

三、线程的阻塞

  好多情况会造成线程的阻塞。大概分成3类:等待阻塞、同步阻塞、人为阻塞。

1、等待阻塞

  等待阻塞是等待wait(),wait/notify/notifyAll是Object对象的内容,会阻塞线程,释放锁。所以这些方法都要写在临界区内。

public class WaitTest {
    public void waitMethod(Object obj){
       try {
           synchronized (obj) {
               obj.wait();
           }
       }catch (InterruptedException e){
           e.printStackTrace();
       }
    }
    public void notifyMethod(Object obj){
        try {
            synchronized (obj) {
                obj.notify();
                Thread.sleep(100);
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
public  class  ThreadA extends  Thread {
    private Object obj;

    public ThreadA(Object obj) {
        this.obj = obj;
    }

    @Override
    public void run() {
        WaitTest waitTest = new WaitTest();
        waitTest.waitMethod(obj);
    }
}
public class ThreadB extends Thread {
    private Object obj;

    public ThreadB(Object obj) {
        this.obj = obj;
    }
    @Override
    public void run(){
        WaitTest waitTest = new WaitTest();
        waitTest.notifyMethod(obj);
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        Object lock = new Object();
        
        Thread A = new ThreadA(lock);
        Thread B = new ThreadB(lock);
        A.start();
        B.start();
    }
}

2、同步阻塞

  同步阻塞是等待同步锁释放,从而回到就绪状态。有synchronized、lock

public class WaitTest {
    public void notifyMethod() {
        synchronized (this) {
            Integer sum = 0;
            for (int i = 1; i < 100; i++) {
                sum++;
            }
        }
    }
}
public  class  MyThread extends  Thread {
    private WaitTest waitTest;
    public MyThread(WaitTest waitTest) {
        this.waitTest = waitTest;
    }
    @Override
    public void run() {
       waitTest.notifyMethod();
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        Object lock = new Object();
        WaitTest waitTest = new WaitTest();
        Thread A = new MyThread(waitTest);
        Thread B = new MyThread(waitTest);
        A.start();
        B.start();
    }
}

3、人为阻塞

  人为阻塞是通过join等方法将线程堵塞,等待返回。有sleep、join、等待I/O等。

public  class  MyThread extends  Thread {
        @Override
    public void run() {
            Integer sum = 0;
            for (int i = 1; i < 100; i++) {
                sum++;
            }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        Thread A = new MyThread();
        A.start();
        try {
            A.join();
            Integer sum = 0;
            for (int i = 1; i < 100; i++) {
                sum++;
            }
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

四、同步的方式

  线程同步,是为了避免不同线程访问共享资源时产生不确定结果。线程通信是指,各个线程协同工作,需要相互通知。线程同步常用同步块和锁,线程通信常用等待和通知。

1、synchronize

  使用synchronize关键字来标记同步块或者时同步方法,当且只有一个线程可以执行。

2、lock

  lock类的灵活性比synchronize好,也是用于同步代码块,做到线程安全访问。

3、wait和notify或notifyAll

  大多数情况下用于线程间通信,相互通知。

4、join

  这种大多数用于需要运行完某个线程,这个做线程安全和线程通信有点鸡肋。

5、yield

  这种更极端,直接线程让步,但是让步不一定能保证线程安全和线程通信安全。

发布了63 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/myt0929/article/details/84316451