Java基础——多线程

时间:2023-02-16 08:07:30

 

1.线程的创建:

  方式一:继承java.lang.Thread类
  方式二:通过实现的方式

  继承的方式 VS 实现的方式:
    1.联系:public class Thread implements Runnable
    2.实现的方式优于继承的方式
      1.避免了Java单继承的局限性
      2.如果多个线程要操作同一份资源(或数据),更适合使用实现的方式

 

package test;

//创建线程的第一种方式:继承java.lang.Thread类
//1.创建一个继承Thread的子类
class SubThread extends Thread {
//2.重写run()方法,方法内实现此子线程要完成的任务
public void run() {
for(int i = 0; i < 100; ++i) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
public class TestThread {
public static void main(String[] args) {
//3.创建一个子类的对象
SubThread st = new SubThread();
//4.调用线程的start()方法:启动此线程;并且调用相应的run()方法
//一个线程只能执行一次start();
//不能通过直接调用Thread实现类对象的run()方法去启动一个线程 st.run();//错误的,相当于主线程内启动的
st.start();

for(int i = 0; i < 100; ++i) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}

}

  

 

package test;
//创建线程的第二种方式:通过实现的方式//1.创建一个实现了Runnable接口的类
class SubThread1 implements Runnable{
//2.实现接口的抽象方法
public void run() {
//子线程执行的代码
for(int i = 0; i < 50; ++i) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}

public class TestThread1 {
public static void main(String[] args) {
//3.创建一个Runnable接口实现类的对象
SubThread1 p = new SubThread1();
//要想启动多线程,必须调用start()
//4.将此方法作为形参传递给Thread类的构造器中,创建Thread类的对象,此对象即为一个线程
Thread t1 = new Thread(p);
//5.调用start()方法,启动线程并执行run()
t1.start();

//再创建一个线程
Thread t2 = new Thread(p); //不用再重新创建p,直接传入Thread即可
t2.start();
}

}

2.Thread的常用方法:
  1.void start(): 启动线程,并执行对象的run()方法
  2.run(): 线程在被调度时执行的操作
  3.String getName(): 返回线程的名称
  4.void setName(String name):设置该线程名称
  5.static currentThread(): 返回当前线程
  6.static void yield():线程让步 调用此方法的线程释放当前CPU的执行权
  7.join() :A线程中调用B线程的join()方法,表示:当执行到此方法,A线程停止执行,知道B线程执行完毕,A线程再接着join()之后的代码执行
  8.static void sleep(long millis):(指定时间:毫秒) 不会释放锁
    令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。
    抛出InterruptedException异常
  9.stop(): 强制线程生命期结束
  10.boolean isAlive():返回boolean,判断线程是否还活着
  11.线程通信:wait()(会释放锁) notify() notifyAll()

 

Java基础——多线程

3.设置线程的优先级:(1 - 10)
  MAX_PRIORITY(10);
  MIN _PRIORITY (1);
  NORM_PRIORITY (5);
  涉及的方法:
    getPriority() :返回线程优先值
    setPriority(int newPriority) :改变线程的优先级
  

  注:线程创建时继承父线程的优先级

多线程程序的优点:
  提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
  提高计算机系统CPU的利用率
  改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改


线程安全:多线程中,存在共享数据
  由于一个线程在操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在了安全问题
  解决:必须让一个线程操作共享数据完毕后,其他线程才有机会参与共享数据的操作

 

4.线程的同步机制:解决线程安全问题
  方式一:同步代码块保证所有线程共用同一把锁
    synchronized(同步监视器:即锁){
      //需要被同步的代码块(即为操作共享数据的代码)
    }
    1.共享数据:多个线程共同操作的同一个数据(变量)
    2.同步监视器(锁):可以由任何一个类的对象来充当。哪个线程获取此监视器,谁就执行大括号里被同步的代码。俗称:锁
      可以这样 Object obj = new Object; (把obj放进去都可以),但是不能是局部变量(不能放在run方法内)

  注:所有线程共用同一个锁,在实现的方式中,考虑同步的话,可以使用this来充当锁。
    但是在继承方式中,慎用this(因为会创建多个对象,this每次指代的不是同一个对象)static Object obj = new Object;
    对于静态方法而言,使用当前类本身充当锁(Singleton.class)


  方式二:同步方法 public synchronized void show(){//操作共享数据的代码}
    将操作共享数据的部分封装为一个方法,且声明为synchronized,即此方法为同步方法,能够保证当其中一个线程执行此方法时,其他线程在外等待,直至此线程执行完此方法
    1.对于非静态方法,同步方法的锁:默认为this。如果使用继承的方式实现多线程,慎用
    2.对于静态方法,如果使用同步,默认锁为:当前类本身 以单例的懒汉式为例,Class clazz = Singleton.class

 

线程同步的弊端:由于同一时间只能有一个线程访问数据,效率变低了。

释放锁的操作:wait()
  当前线程的同步方法、同步代码块执行结束
  当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行。
  当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
  当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。
不会释放锁的操作:sleep()、yield()、suspend()
  线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行
  线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)。
  应尽量避免使用suspend()和resume()来控制线程

死锁:

线程通信:wait() 与 notify() 和 notifyAll()
  wait():令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问
  notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
  notifyAll():唤醒正在排队等待资源的所有线程结束等待.

注:Java.lang.Object提供的这三个方法
只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常