上面的代码执行时,有三个线程,首先是主线程main创建2个线程对象,并开启这两个线程任务,开启两个线程后主线程输出"main thread over",然后main线程结束。在开启两个线程任务后,这两个线程加入到了就绪队列等待CPU的调度执行。如下图。因为每个线程被cpu调度是随机的,执行时间也是随机的,所以即使mt1先开启任务,但mt2可能会比mt1线程先执行,也可能更先消亡。
创建线程方式二:
实现Runnable接口,并重写run()方法。
创建子类对象。
创建Thread对象来创建线程对象,并将实现了Runnable接口的对象作为参数传递给Thread()构造方法。
调用start()方法开启线程来执行run()中的任务。
class MyThread implements Runnable { String name; String gender; MyThread(String name,String gender){ this.name = name; this.gender = gender; } public void run(){ int i = 0; while(i<=200) { System.out.println(Thread.currentThread().getName()+"-----"+i); i++; } } } public class CreateThread2 { public static void main(String[] args) { //创建子类对象 MyThread mt = new MyThread("malong","Male"); //创建线程对象 Thread th1 = new Thread(mt); Thread th2 = new Thread(mt); th1.start(); th2.start(); System.out.println("main thread over"); } }这两种创建线程的方法,无疑第二种(实现Runnable接口)要好一些,因为第一种创建方法继承了Thread后就无法继承其他父类。
3.线程相关的常用方法Thread类中的方法:
isAlive():判断线程是否还活着。活着的概念是指是否消亡了,对于运行态、就绪态、睡眠态的线程都是活着的状态。
currentThread():返回值为Thread,返回当前线程对象。
getName():获取当前线程的线程名称。
setName():设置线程名称。给线程命名还可以使用构造方法Thread(String thread_name)或Thread(Runnable r,String thread_name)。
getPriority():获取线程优先级。优先级范围值为1-10(默认值为5),相邻值之间的差距对cpu调度的影响很小。一般使用3个字段MIN_PRIORITY、NORM_PRIORITY、MAX_PRIORITY分别表示1、5、10三个优先级,这三个优先级可较大地区分cpu的调度。
setPriority():设置线程优先级。
run():封装的是线程开启后要执行的任务代码。如果run()中没有任何代码,则线程不做任何事情。
start():开启线程并让线程开始执行run()中的任务。
toString():返回线程的名称���优先级和线程组。
sleep(long millis):让线程睡眠多少毫秒。
join(t1):将线程t1合并到当前线程,并等待线程t1执行完毕后才继续执行当前线程。即让t1线程强制插队到当前线程的前面并等待t1完成。
yield():将当前正在执行的线程退让出去,以让就绪队列中的其他线程有更大的几率被cpu调度。即强制自己放弃cpu,并将自己放入就绪队列。由于自己也在就绪队列中,所以即使此刻自己放弃了cpu,下一次还是可能会立即被cpu选中调度。但毕竟给了机会给其它就绪态线程,所以其他就绪态线程被选中的几率要更大一些。
Object类中的方法:
wait():线程进入某个线程池中并进入睡眠态。等待notify()或notifyAll()的唤醒。
notify():从某个线程池中随机唤醒一个睡眠态的线程。
notifyAll():唤醒某个线程池中所有的睡眠态线程。
这里的某个线程池是由锁对象决定的。持有相同锁对象的线程属于同一个线程池。见后文。
一般来说,wait()和唤醒的notify()或notifyAll()是成对出现的,否则很容易出现死锁。
sleep()和wait()的区别:(1)所属类不同:sleep()在Thread类中,wait()则是在Object中;(2)sleep()可以指定睡眠时间,wait()虽然也可以指定睡眠时间,但大多数时候都不会去指定;(3)sleep()不会抛异常,而wait()会抛异常;(4)sleep()可以在任何地方使用,而wait()必须在同步代码块或同步函数中使用;(5)最大的区别是sleep()睡眠时不会释放锁,不会进入特定的线程池,在睡眠时间结束后自动苏醒并继续往下执行任务,而wait()睡眠时会释放锁,进入线程池,等待notify()或notifyAll()的唤醒。
java.util.concurrent.locks包中的类和它们的方法:
Lock类中:
lock():获取锁(互斥锁)。
unlock():释放锁。
newCondition():创建关联此lock对象的Condition对象。
Condition类中:
await():和wait()一样。
signal():和notify()一样。
signalAll():和notifyAll()一样。
4.多线程安全问题和线程同步 4.1 多线程安全问题线程安全问题是指多线程同时执行时,对同一资源的并发操作会导致资源数据的混乱。