线程、多线程和线程池,我终于把这些全部搞懂了
1、开启线程的三种方式? 1)继承Thread类,重写run()方法,在run()方法体中编写要完成的任务 new Thread().start(); 2)实现Runnable接口,实现run()方法 new Thread(new MyRunnable()).start(); 3)实现Callable接口MyCallable类,实现call()方法,使用FutureTask类来包装Callable对象,使用FutureTask对象作为Thread对象的target创建并启动线程;调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
2、run()和start()方法区别 run()方法只是线程的主体方法,和普通方法一样,不会创建新的线程。只有调用start()方法,才会启动一个新的线程,新线程才会调用run()方法,线程才会开始执行。 3、如何控制某个方法允许并发访问线程的个数? 创建Semaphore变量,Semaphore semaphore = new Semaphore(5, true); 当方法进入时,请求一个信号,如果信号被用完则等待,方法运行完,释放一个信号,释放的信号新的线程就可以使用。 4、在Java中wait和seelp方法的不同 wait()方法属于Object类,调用该方法时,线程会放弃对象锁,只有该对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。 sleep()方法属于Thread类,sleep()导致程序暂停执行指定的时间,让出CPU,但它的监控状态依然保存着,当指定时间到了又会回到运行状态,sleep()方法中线程不会释放对象锁。 5、谈谈wait/notify关键字的理解 notify: 唤醒在此对象监视器上等待的单个线程 notifyAll(): 通知所有等待该竞争资源的线程 wait: 释放obj的锁,导致当前的线程等待,直接其他线程调用此对象的notify()或notifyAll()方法 当要调用wait()或notify()/notifyAll()方法时,一定要对竞争资源进行加锁,一般放到synchronized(obj)代码中。 当调用obj.notify/notifyAll后,调用线程依旧持有obj锁,因此等待线程虽被唤醒,但仍无法获得obj锁,直到调用线程退出synchronized块,释放obj锁后,其他等待线程才有机会获得锁继续执行 6、什么导致线程阻塞? 一般线程阻塞 1)线程执行了Thread.sleep(int millsecond)方法,放弃CPU,睡眠一段时间,一段时间过后恢复执行; 2)线程执行一段同步代码,但无法获得相关的同步锁,只能进入阻塞状态,等到获取到同步锁,才能恢复执行; 3)线程执行了一个对象的wait()方法,直接进入阻塞态,等待其他线程执行notify()/notifyAll()操作; 4)线程执行某些IO操作,因为等待相关资源而进入了阻塞态,如System.in,但没有收到键盘的输入,则进入阻塞态。 5)线程礼让,Thread.yield()方法,暂停当前正在执行的线程对象,把执行机会让给相同或更高优先级的线程,但并不会使线程进入阻塞态,线程仍处于可执行态,随时可能再次分得CPU时间。 线程自闭,join()方法,在当前线程调用另一个线程的join()方法,则当前线程进入阻塞态,直到另一个线程运行结束,当前线程再由阻塞转为就绪态。 6)线程执行suspend()使线程进入阻塞态,必须resume()方法被调用,才能使线程重新进入可执行状态 7、线程如何关闭? 1 ) 使用标志位 2)使用stop()方法,但该方法就像关掉电脑电源一样,可能会发生预料不到的问题 3)使用中断interrupt()
但调用interrupt()方法只是传递中断请求消息,并不代表要立马停止目标线程。 8、讲一下java中的同步的方法 之所以需要同步,因为在多线程并发控制,当多个线程同时操作一个可共享的资源时,如果没有采取同步机制,将会导致数据不准确,因此需要加入同步锁,确保在该线程没有完成操作前被其他线程调用,从而保证该变量的唯一一性和准确性。 1)synchronized修饰同步代码块或方法 由于java的每个对象都有一个内置锁,用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需获得内置锁,否则就处于阴塞状态。 2)volatile修饰变量 保证变量在线程间的可见性,每次线程要访问volatile修饰的变量时都从内存中读取,而不缓存中,这样每个线程访问到的变量都是一样的。且使用内存屏障。 3)ReentrantLock重入锁,它常用的方法有ReentrantLock():创建一个ReentrantLock实例 lock()获得锁 unlock()释放锁 4)使用局部变量ThreadLocal实现线程同步,每个线程都会保存一份该变量的副本,副本之间相互独立,这样每个线程都可以随意修改自己的副本,而不影响其他线程。 常用方法ThreadLocal()创建一个线程本地变量;get()返回此线程局部的当前线程副本变量;initialValue()返回此线程局部变量的当前线程的初始值;set(T value)将此线程变量的当前线程副本中的值设置为value 使用原子变量,如AtomicInteger,常用方法AtomicInteger(int value)创建个有给定初始值的AtomicInteger整数;addAndGet(int data)以原子方式将给定值与当前值相加 6)使用阻塞队列实现线程同步LinkedBlockingQueue 9、如何保证线程安全? 线程安全性体现在三方法: 1)原子性:提供互斥访问,同一时刻只能有一个线和至数据进行操作。 (编辑:ASP站长网) |