Java知识整理——多线程

(1)、java命令会启动java虚拟机(jvm),相当于启动了一个应用程序,也就是启动了一个进程。然后该进程启动一个主线程,由主线程调用某个类的mian方法,所以main方法运行在主线程中的,前面的程序都是单线程程序。

(2)、jvm虚拟机启动是多线程的。因为垃圾回收线程也启动,否则就很容易出现内存溢出。一个主线程加上垃圾回收线程,至少都是2个线程,所以,jvm启动是多线程的。

(3)、线程的创建如何实现?

因为线程是依赖进程存在的,所以我们要先创建一个进程。进程是由系统创建的,所以我们要去调用系统功能创建一个进程,java是不能直接调用系统功能的。所以我们无法直接实现多线程程序。

但是,java可以去调用c/c++写好的程序来实现多线程程序。由c/c++去调用系统功能创建进程,然后java调用它。在java中提供了一些类来供我们使用。我们就可以实现多线程了

(3)、为什么要重写run方法?

不是说类中的所有代码都需要被线程执行的。所以,为了区分那些要被线程执行的代码,java就提供了Thread类,在这个类中提供了run()方法用来包含那些需要被线程执行的代码。

一般来说,被线程执行的代码肯定比较耗时的

(5)、常用方法

构造:new MyThread("119");设置线程名称

普通:public final String getName()

public final void setName(String name)

public static Thread currentThread();返回当前正在执行的线程对象

getName();获取所在的线程名称

Thread.currentThread().getName();

(6)、为什么默认的线程名称是  Thread-编号?

class Thread{
	private String name;
	private static int threadInitNumber;
 
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;//0
    }
	 public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
	
	 private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }
	
	 private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        this.name = name;
		//.....
    }
	
	   public final String getName() {
        return name;
    }
}

(7)、调用main方法的线程的名称是什么?main

(8)、两种方式的对比

实现Runnable接口的方式创建的线程可以处理同一资源,从而实现资源的共享。

联系:class Thread implements Runnable

实现的方式较好:①解决了单继承的局限性。②适合多个相同程序代码去处理同一个资源的情况。

(9)、售票程序模拟

public class Demo {
    public static void main(String[] args) {
/*    new TicketWindow().start();
      new TicketWindow().start();
      new TicketWindow().start();
      new TicketWindow().start();
      new TicketWindow().start();*/
        //由于现实中铁路系统中的票资源是共享的,因此上述不合理,为了保证资源共享只能创建一个售票对象,然后开启多个线程去运行一个售票对象中的售票方法
        TicketWindow m = new TicketWindow();
        new Thread(m,"窗口1").start();
        new Thread(m,"窗口2").start();
        new Thread(m,"窗口3").start();
        new Thread(m,"窗口4").start();
    }
}
class TicketWindow extends Thread{
    private int tickets = 100;
    public void run() {
        while(tickets>0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Thread th = Thread.currentThread();
            String name = th.getName();
            System.out.println(name+"正在发售第"+tickets--+"张票");
        }
    }
}
//会出现负数票

(10)、线程的调度

线程调度有两种模型:分时调度模型和抢占式模型(Java虚拟机默认抢占式模型)

线程的优先级

最高优先级:static int MAX_PRIORITY (10)  

最低优先级:static int MIN _PRIORITY (1)  

默认优先级:static int NORM_PRIORITY (5)

线程的优先级控制

public final int getPriority() :返回线程优先值

public final void setPriority(int newPriority):改变线程的优先级

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

(11)、多线程常用方法

Thread类下方法

setName(String name)

更改线程名称

getName()

返回此线程的名称

isAlive()

测试这个线程是否活着,返回true和false

join()

当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止   

低优先级的线程也可以获得执行

stop()

强制线程生命期结束

currentThread()

返回当前线程,常用Thread.currentThread()

public final void setDaemon(boolean on)

将该线程标记为守护线程或用户线程

public void interrupt()

中断进程

Object类中涉及的线程操作方法

wait(long timeout)

导致当前线程等待,直到另一个线程调用 notify()方法或该对象的 notifyAll()方法,或者指定的时间已过。

wait(long timeout, int nanos)

导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法,或者某些其他线程中断当前线程,或一定量的实时时间。

(12)、如下的三个方法必须使用在同步代码块或同步方法中!

wait():当在同步中,执行到此方法,则此线程“等待”,直至其他线程执行

notify()的方法,将其唤醒,唤醒后继续其wait()后的代码

notify()/notifyAll():在同步中,执行到此方法,则唤醒其他的某一个或所有的被wait的线程。

(13)、Java中的线程分为两类:一种是守护线程,一种是用户线程。

(14)、为什么wait()必须在同步(Synchronized)方法/代码块中调用?

答:调用wait()就是释放锁,释放锁的前提是必须要先获得锁,先获得锁才能释放锁。

(15)、为什么notify(),notifyAll()必须在同步(Synchronized)方法/代码块中调用?

答:notify(),notifyAll()是将锁交给含有wait()方法的线程,让其继续执行下去,如果自身没有锁,怎么叫把锁交给其他线程呢;(本质是让处于入口队列的线程竞争锁)

(16)、synchronized与Lock的区别

首先synchronized是java内置关键字,在jvm层面,Lock是个java类;

synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;

synchronized会自动释放锁,Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;

用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;

synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)

Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

(17)、死锁

同步的缺点:效率低,如果出现了同步嵌套,就会产生死锁问题。

死锁:指2个或2个以上的线程在执行的过程中,因为争夺资源产生的一种相互等待的现象。

public class Demo5 {
	public static void main(String[] args) {
		DieLock dl1 = new DieLock(true);
		DieLock dl2 = new DieLock(false);
		dl1.start();
		dl2.start();
	}
}
public class DieLock extends Thread{
	private boolean boo;
	public DieLock(boolean boo) {
		this.boo = boo;
	}
	public void run() {
		if(boo) {//t1
			synchronized (MyLock.obj1) {
				System.out.println("obj1");//t1
				synchronized (MyLock.obj2) {
					System.out.println("obj2");
				}
			}
		}else {//t2
			synchronized (MyLock.obj2) {
				System.out.println("obj22");//t2
				synchronized (MyLock.obj1) {
					System.out.println("obj11");
				}
			}
		}
	}
}
public class MyLock {
	public static Object obj1 = new Object();
	public static Object obj2 = new Object();
}

(18)、线程组:把多个线程组合到一起

在java中可以使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,可以直接对线程组进行控制。默认情况下,所有的线程都属于主线程组

Thread类提供几个构造器来创建新的线程属于哪个线程组

Thread(ThreadGroup group,Runnable target):以target的run()方法为线程执行提创建新线程,新线程属于线程组group。

Thread(ThreadGroup group,Runnable target,String name):以target的run()方法为线程执行提创建新线程,并且输入group线程组,新线程名字为name。

Thread(ThreadGroup,String name):创建新线程,属于group线程组,新线程名字为name。

ThreadGroup类的简单构造器以及几个常用方法:

ThreadGroup(String name):创建名为name的新线程组。

ThreadGroup(ThreadGroup parent,String name):创建指定父线程组,名为name的新线程组。

int activeCount():返回此线程组中活动线程的数目。

interrupt():中断此线程组中所有的线程。

isDaemon():判断此线程组是否为后台线程。

setDaemon(boolean daemon):设置线程组为后台线程(当后台线程组中最后一个线程执行完成活着最后一个线程被销毁,后台线程将自动销毁)。

setMaxPriority(int prio):设置线程组的最高优先级。

线程对象关联线程组:

1级关联。介绍:1级关联就是父对象中有子对象,但并不创建子孙对象。

多级关联。介绍:多级关联就是父对象中有子对象,子对象中再创建子对象,也就是有子孙对象。如果关系太复杂反而不利于开发管理,所以多级关联不常见。

(19)、线程池

为提高程序的性能,效率。尤其当程序需要大量的生命周期比较短的线程时,就更应该考虑使用线程池。线程池中每一个线程在代码执行结束后,它不会死亡,而是会重新回到线程池中等待下一次被使用。

在java中有这么一个Executors工厂类可以生产线程池。

public static ExecutorService newCachedThreadPool(),创建有缓存功能的线程池

public static ExecutorService newFixedThreadPool(int nThreads),创建一个可以重用的,有指定线程数的线程池

public static ExecutorService newSingleThreadExecutor(),创建一个只有一个线程的线程池。

ExecutorService:这就是一个线程池对象,可以执行Runnable对象或Callable对象所表示的线程。

如何创建和使用线程池

创建线程池对象:ExecutorService threadPool = Executors.newFixedThreadPool(5);

可以执行Runnable对象或Callable对象所表示的线程:threadPool.submit(new MyThread());

关闭线程池:threadPool.shutdown();

(20)、定时器

是一个应用很广泛的工具,可以调度多个定时任务以后台线程的方式执行,java就提供了Timer 和 TimerTask 类来实现调度功能。

Timer

Timer() ,创建一个新计时器。 void schedule(TimerTask task, Date firstTime, long period) 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。 void schedule(TimerTask task, long delay) ,安排在指定延迟后执行指定的任务。

TimerTask

boolean cancel(),取消此计时器任务。 abstract void run(),此计时器任务要执行的操作

示例:

public static class Demo{
    public static void main(String[]args){
        TimerTask tsak = new TimerTask() {
            @Override
            public void run() {
                System.out.println("秋招!必胜!");
            }
        };
        Timer timer = new Timer();
        timer.schedule(tsak,10000);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值