- 创建多线程的第一种方式:继承Thread类,并重写其run方法。然后通过创建Thread类的子类对象,并调用start()方法,就开启了一个新线程。
- Thread类中有关线程名称比较常用的方法:
public Thread()
public Thread(String name)
public final String getName()
public final synchronized void setName(String name)
public static native Thread currentThread()
public final int getPriority()
public final void setPriority(int newPriority)
public static native void sleep(long millis) throws InterruptedException
public final void join() throws InterruptedException
public static void yield()
public final void setDaemon(boolean on)
- 线程生命周期:
- 线程状态:新建(创建线程对象)、就绪(有执行资格,没有执行权)、运行(有执行资格,有执行权)、死亡(线程对象变成垃圾,等待被回收)、阻塞(没有执行资格,没有执行权)。
- 新建到就绪通过start方法;运行到阻塞通过sleep或wait方法;阻塞到就绪通过notify方法或者sleep方法执行完毕;运行与就绪状态之间可以双向切换,因为多线程抢占资源;运行到死亡通过interrupt方法或者run方法执行完毕。
- 创建多线程的第二种方式:实现Runnable接口,重写其中的run方法。创建该子类对象,并将其作为创建Thread对象有参构造方法的参数传递。调用Thread对象的start()方法,就开启了一个新线程。
public Thread(Runnable target)
public Thread(Runnable target, String name)
- 第二种创建多线程的方式更好:
- 可以避免由于Java单继承带来的局限性。
- 适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码、数据有效分离,较好的体现了面向对象的设计思想。
- 由于多个线程操作同样的数据资源,所以有可能会产生线程不同步也就是线程安全问题。为了解决该问题,提供了同步代码块的解决方案:
synchronized(任意对象){
需要被锁起来的代码块
}
- 也可以将一个方法中的代码都锁起来,只需要在方法前面加上修饰符synchronized,默认的对象是this。
- 静态方法也能够通过修饰符synchronized锁起来,其默认的对象是类的字节码文件对象。
- Collections类中的
public static <T> List<T> synchronizedList(List<T> list)
等方法能够实现将非同步的列表、集合、Map转换成线程同步的。 - 除了通过synchronized关键字修饰,JDK5之后提供了一种新的对代码块进行加锁的方法。通过Lock接口的子类对象调用lock()方法和unlock()方法来对一段代码进行包裹。
- 同步的弊端:效率低、容易产生死锁。
- 死锁:两个或者两个以上线程在争夺资源过程中,发生的一种互相等待的现象。
public class TestDemo {
public static void main(String[] args) {
DieLock dl1 = new DieLock(true);
DieLock dl2 = new DieLock(false);
dl1.start();
dl2.start();
}
}
class DieLock extends Thread {
private boolean flag;
DieLock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (MyLock.objA) {
System.out.println("if objA");
synchronized (MyLock.objB) {
System.out.println("if objB");
}
}
} else {
synchronized (MyLock.objB) {
System.out.println("else objB");
synchronized (MyLock.objA) {
System.out.println("else objA");
}
}
}
}
}
class MyLock {
static final Object objA = new Object();
static final Object objB = new Object();
}
- Object类中与线程相关的方法:wait、notify。这两个方法的调用线程必须是处于运行状态。
- wait方法能够使当前线程立即释放锁并进入等待阻塞状态,直到另一个线程通过调用锁对象的notify()方法或notifyAll()方法将其唤醒。被唤醒的线程不会立即开始执行,而是在锁不被占用时再与所有处于就绪状态的线程进行竞争,当争得执行权时,会从上次暂停的位置继续执行。
- 线程组能够在成批管理线程时发挥作用。通过Thread类的getThreadGroup方法能够获得线程所在的线程组ThreadGroup对象。而ThreadGroup类中的setDaemon方法、interrupt方法等能够实现对线程组线程的统一操作。getName方法可以获取线程组的名称。在创建线程时,有一个构造方法
public Thread(ThreadGroup group, Runnable target, String name)
能够将创建的线程加入指定的线程组,默认线程组是main线程组。 - 线程池相关的Executors工厂类能够创建一个线程池对象:
public static ExecutorService newFixedThreadPool(int nThreads)
- ExecutorService类的
Future<?> submit(Runnable task)
方法能能够接收一个Runnable接口的子类对象,并启动线程池的一个线程来运行run方法。该方法相当于将前面创建线程和start方法启动线程两步的效果。 - ExecutorService类的
<T> Future<T> submit(Callable<T> task)
方法跟前面的方法相似,接收的是Callable接口的子类对象,其中泛型的类型是其中call方法的返回值类型。call方法就相当于前面的run方法,其中写上被多线程执行的代码,与前面run方法有所不同的是该方法有返回值。submit方法的返回值是Future接口的子类对象,通过调用该对象的get方法能够得到call方法的执行结果。 - 定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定时调度的功能。
public Timer()
public void schedule(TimerTask task, long delay)
public void schedule(TimerTask task,long delay,long period)
public abstract void run()