多线程
多线程的创建
创建多线程的方式一:继承于Thread类**
- 1.创建一个继承于Thread类的子类
- 2.重写Thread的run()方法 —> 将此线程的方法声明在run()中
- 3.创建Thread类的子对象
- 4.通过此对象调用start()
class myThread extends Thread{ //1.创建一个继承于Thread类的子类
@Override
public void run() { //2.重写Thread的run()方法 ---> 将此线程的方法声明在run()中
for (int i = 1; i < 100; i++){
if(i%2 == 0){
System.out.println(Thread.currentThread().getName()+":" + i);
}
}
}
}
public class ttt {
public static void main(String[] args) throws InterruptedException {
myThread mt = new myThread(); // 3.创建Thread类的子对象
mt.setName("我先跑");
mt.start(); //4.通过此对象调用start()
Thread.currentThread().setName("我后跑");
for (int i = 0; i < 100; i++) {
mt.join();//先跑的卡住后跑的
if (i%2==0){
System.out.println(Thread.currentThread().getName()+ ":" + i); //currentThread()返回当前线程
}
}
}
}
创建多线程的方式二:实现Runnable接口
- 1.创建一个实现了Runnable接口的类
- 2.实现类去实现Runnable中的抽象方法:run()
- 3.创建实现类的对象
- 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
也就是说将重写完的run方法 作为参数 创建对象 - 5.通过Thread类的对象调用start()
package service;
/**
* @Classname ttt
* @Description TODO
* @Date 2022/1/3 23:17
* @Created by asus
*/
public class ttt {
public static void main(String[] args) {
myThread mt = new myThread(); // 3.创建 实现类 的对象
Thread thread = new Thread(mt); //4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
thread.setName("Runnable");
thread.start();
}
}
class myThread implements Runnable{ // 1.创建一个实现了Runnable接口的类
@Override
public void run() { //2.实现类去实现Runnable中的抽象方法:run()
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread() + ":" + i);
}
}
}
}
线程的五种状态:
- 新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
- 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
- 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能
- 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态
- 死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
进程同步:由于出现了多线程,同时处理同一个、多个数据,因此需要一个锁,保证在同一时间只有一个线程在处理共享数据。
synchronized (任意对象){
处理共享数据的代码 //同步代码块
}
通过锁的方式解决进程同步:
private ReentrantLock lock = new ReentrantLock();
try{
lock.lock();//挂锁
代码块
}finally{
lock.unlock();//解锁 将解锁放在funally中保证一定能解锁
}
synchronized | lock |
---|---|
被动解锁 | 主动解锁(手动解锁) |
wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
说明:
1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则,会出现IllegalMonitorStateException异常
3.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。**
sleep() 和 wait()的异同?
- 1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
- 2.不同点:
1)两个方法声明的位置不同:Thread类中声明sleep() , Object类中声明wait()
2)调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中(synchronized)
3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。
创建多线程的方式三:实现Callable接口 —> JDK 5.0新增
1.创建一个实现Callable的实现类
2.实现call方法,将此线程需要执行的操作声明在call()中
3.创建Callable接口实现类的对象
4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
6.获取Callable中call方法的返回值 --也可不获取
创建多线程的方式四:使用线程池
好处:
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3.便于线程管理
1.创建一个线程池
2.创建一个实现了Runnable或Callable的实现类,重写run方法
3.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread()); //适合适用于Runable
service.submit(Callable callable);//适合适用于Callable
public class ttt {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new myThread1());
System.out.println("********");
service.execute(new myThread2());
service.shutdown();
}
}
class myThread1 implements Runnable{ // 1.创建一个实现了Runnable接口的类
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
class myThread2 implements Runnable{ // 1.创建一个实现了Runnable接口的类
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
System.out.println(i*10);
}
}
}
service.execute(new NumberThread());
根据不同的实现类完成不同的操作。