多线程

一.线程的创建

1.方式一:继承Thread类

问题1

我们不能通过对象直接调用run()的方法启动线程

只有start才可以启动线程。

问题2

再启动一个线程,遍历1-100以内的偶数?

        不可以让已经start()的线程去执行,会报IllegalThreadStateException的异常;

我们需要重新创建一个线程的对象去执行

/**
* 多线程的创建,方式一:继承与Thread类
* 1.创建一个继承与Thread类的子类
* 2.重写Thread类的run方法   -->将此线程执行的操作声明在run方法中
* 3.创建Thread类的子类的对象
* 4.通过此对象调用start()
*
* 例子:遍历100以内的所有的偶数
*/
class MyThread extends Thread{
   @Override
   public void run() {
       for (int i = 0; i <100 ; i++) {
           if (i%2==0){
               System.out.println(i);
          }
      }
  }
}
public class TheradText {
   public static void main(String[] args) {
       MyThread t1 = new MyThread();
       t1.start();//创建的线程执行
       //t1.run();//如果直接调用重写的run方法,并不是在新建的线程中执行,任然实在main主线程中执行
       for (int i = 0; i < 100; i++) {
           if (i % 2 != 0) {
               System.out.println(i + "**********************");//main主线程执行
          }
      }
  }
}

2.方式二:实现Runnable接口

package Thread;

/**
* <h5>描述:创建多线程的方式二:实现Runnable接口</h5>
* 1.创建一个实现了Runnable接口的类
* 2.实现类去实现Runnable中的抽象方法:run()
* 3.创建实现类的对象
* 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5.通过Thread类的对象调start()
*/
class MThread implements Runnable{
   @Override
   public void run() {
       for (int i = 0; i < 100; i++) {
           if (i %2==0){
               System.out.println(i);
          }
      }
  }
}
public class ThreadDemo02 {
   public static void main(String[] args) {
       MThread m  = new MThread();
       Thread t = new Thread(m);
       t.start();
  }
}

3.比较两种创建多线程的方式

开发中:优先选择实现Runnab的方式

原因:1.实现的方式没有类的单继承的局限性。

       2.实现的方式更适合来处理多个线程有共享数据的情况。

联系:Thread的类本身也实现了Runnable。

相同点:两种方式都不需要重写run方法。

4.方式三:实现Callable接口(JDK5.0新增)

package Thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
* <h5>描述:</h5>
* 实现Callable接口
*/
public class ThreadNew {
   public static void main(String[] args) {
      NumThread n1=new NumThread();
       FutureTask f1 = new FutureTask(n1);
       try {
           //get方法的返回值即为FutureTask构造器参数Callable实现类重写的call方法的返回值
           Object sum = f1.get();
           System.out.println(sum);
      } catch (InterruptedException e) {
           e.printStackTrace();
      } catch (ExecutionException e) {
           e.printStackTrace();
      }
       new Thread(f1).start();
  }
}
class NumThread implements Callable{
   @Override
   public Object call() throws Exception {
       int sum=0;
       for (int i = 0; i <= 100; i++) {
           if (i%2==0){
               sum+=i;
          }
      }
       return sum;
  }
}

5.使用线程池(JDK5.0新增)

package Thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

/**
* <h5>描述:</h5>
* 创建线程的方式四:使用线程池
*/
class NumberThread implements Runnable{
   @Override
   public void run() {
       for (int i = 0; i <= 10; i++) {
           if (i%2==0){
               System.out.println(Thread.currentThread().getName()+i);
          }
      }
  }
}
public class ThreadPoor {
   public static void main(String[] args) {
       ExecutorService service;
       service = Executors.newFixedThreadPool(10);
       //ExecutorService是一个接口,属性不多,需要将接口向下转型为他的类ThreadPoolExecutor才可以设置线程池的属性
       ThreadPoolExecutor service1  = (ThreadPoolExecutor) service;
       service1.setCorePoolSize(15);

       service.execute(new NumberThread());
       service.shutdown();//适合适用于Runnable
       //service.submit();//适合适用于Callable
  }
}

 

二.Thread常用的方法

1.Thread.currentThread().setName()

Thread.currentThread().getName()

设置并获取当前线程的名字

public class ThreadMethodText {
   public static void main(String[] args) {
       HelloThread t1=new HelloThread();
       t1.setName("线程一");
       t1.start();
       Thread.currentThread().setName("主线程");
       for (int i = 0; i < 100; i++) {
           if (i%2==0){
               System.out.println(Thread.currentThread().getName()+":"+i);
          }
      }

  }
}
class HelloThread extends Thread{
   @Override
   public void run() {
       for (int i = 0; i < 100; i++) {
           if (i%2==0){
               System.out.println(Thread.currentThread().getName()+":"+i);
          }
      }
  }
}

2.yield

释放执行权,线程让步

3.join

在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态.

public class ThreadMethodText {
   public static void main(String[] args) {
       HelloThread t1=new HelloThread();
       t1.setName("线程一");
       t1.start();
       Thread.currentThread().setName("主线程");
       for (int i = 0; i < 100; i++) {
           if (i%2==0){
               System.out.println(Thread.currentThread().getName()+":"+i);
          }
           if (i==0){
               try {
                   t1.join();//当i==0时,主线程进入阻塞状态,线程一完全执行完以后主线程结束阻塞
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
      }

4.stop()

已过时,强行结束当前线程的生命周期

5.sleep(Long millitime);

睡眠,让当前线程睡眠指定的millitime毫秒

class HelloThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
try {
sleep(1000);//让当前线程阻塞一秒,一秒后等待下次CPU分配资源才开始执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + i);
}

}

}
}

6.siAlive():

判断当前线程是否还有生命周期

三.线程的优先级

  1. MAX_PRIORITY:10

    MIN_PRIOPITY:1

    NORM_PRIORITY:5

  2. 获取和设置当前线程的优先级:

    getPriority():获取线程的优先级;

    setPriority(int P):设置线程的优先级

    说明:优先级高只是概率上高优先级的先执行。

四.线程同步

1.synchronized(){}

(1)大括号中填入对共享数据操作的代码块

(2)小括号中填入任意的对象,在实现Runnable接口中可以考虑填入this,在继承Thread接口中可以考虑使用类来充当对象。

2.synchronized直接修饰方法

3.Lock锁

private  ReentrantLock lock=new ReentrantLock;
//使用try{}将要需要同步的代码包裹起来
//然后再try包裹的结构中调用lock.lock方法
//最后添加finall{lock.unlock}方法

 

四.线程通信

1.方法

wait()方法:一旦执行此方法,当前线程就会进入阻塞状态,并释放同步监视器;

notify()方法:一旦执行此方法,就会唤醒呗wait的一个线程。如果有多个线程,就唤醒优先级高的;

notifyAll()方法:一旦执行此方法,就会唤醒所有呗wait的线程。

2.说明

(1).wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中;

(2)wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则,会出现I了legalMonitorStateExceptionyic的异常;

(3)wait(),notify(),notifyAll()三个方法是定义在Object类当中的。

五面试题:

sleep()和wait()的异同

1.相同点:

一旦执行方法,都可以是当前的线程进入阻塞状态

2.不同点:

1)两个方法声明的位置不同:Thread类中声明sleep(),Object类中定义wait();

2)调用的要求不同:sleep()可以在任课需要的场景下调用。wait()必须使用在同步代码块和同步方法中;

3)关于是否释放同步监视器:如果两个方法都是用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。

 



posted @ 2022-04-13 15:34  与否业务NOW  阅读(24)  评论(0)    收藏  举报