线程简介:
线程是一个程序内部的顺序控制流。
线程和进程的区别:
每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。
线程可以看成是轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器,线程切换的开销小。
多进程:
在操作系统中能同时运行的多个任务。
多线程:
在同一个应用程序中有多个顺序流同时执行。
Java的线程是通过java.lang.Thread类实现的;JVM启动时会有一个由主方法所定义的线程;可以通过创建Thread的实例来创建新的线程;每个线程都是通过某个特定的Thread对象所对应的run()方法来完成其操作的,run()方法称为线程体。
线程的创建与启动:
第一种:
1.定义线程类target实现Runnable接口(使用Runnable接口可以为多个线程提供共享数据),其中Runnable中只有一个方法public void run();
2.Thread my Thread = new Thread(target);
3.在实现Runnable接口的类的run()方法定义中可以使用Thread的静态方法(currectThread()方法用于获取当前线程的引用)。
第二种:
1.可以定义一个Thread的子类并重写其run()方法:class MyThread extends Thread{ public void run(){ } } ;
2.然后生成该类的对象。
线程状态的转换:
阻塞解除 <----------------- 调度 导致阻塞的事件 <---- ---->*阻塞状态 *创建 *就绪状态 *运行状态 *终止 ----> ----> ----> start 调度 运行结束 *标记为线程状态,箭头表示转换过程。
线程控制的基本方法:
isAlive() 判断线程是否还活着,即线程还未终止(上面的阻塞、就绪、运行三个状态)
getPriority() 获得线程的优先级
setPriority() 设置线程的优先级
Thread.sleep() 使当前线程进入休眠状态,并指定睡眠的毫秒数
join() 将当前线程与被调用该方法的线程合并,即等待该线程结束,在回复当前线程的运行
yield() 让出cpu,当前线程进入就绪队列等待调度
wait() 当前线程进入对象的wait pool
notify()/notifyAll() 唤醒对象的wait pool中的一个/所以等待线程
join的例子:
public class TestJoin { public static void main(String[] args) { MyThread2 t1 = new MyThread2("abcde"); t1.start(); try { t1.join(); } catch (InterruptedException e) {} for(int i=1;i<=10;i++){ System.out.println("i am main thread"); } } } class MyThread2 extends Thread { MyThread2(String s){ super(s); } public void run(){ for(int i =1;i<=10;i++){ System.out.println("i am "+getName()); try { sleep(1000); } catch (InterruptedException e) { return; } } } }
只有执行完了t1的run()方法时才会继续执行main()方法。
Java的优先级别:
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照线程的优先级决定应调用哪个线程来执行;线程的优先级用数字来表示,范围从1到10,一个线程的缺省优先级是5。
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5
线程同步:
在Java语言中,引入对象互斥锁的概念,保证共享数据操作的完整性;每个对象都对应于一个可以成为互斥锁的标记;这个标记保证在任意时刻,只有一个线程访问该对象;关键字synchronized用来与对象的互斥锁相联系;当某个对象的synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。
synchronized的用法:
synchronized (this) { }
synchronized还可以放在方法生声明中,表示整个方法为同步方法:
public synchronized void add(String name){ }
无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。
public class TestSync implements Runnable { Timer timer = new Timer(); public static void main(String[] args) { TestSync test = new TestSync(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.setName("t1"); t2.setName("t2"); t1.start(); t2.start(); } public void run(){ timer.add(Thread.currentThread().getName()); } } class Timer{ private static int num = 0; public synchronized void add(String name){ //synchronized (this) { num ++; try {Thread.sleep(1);} catch (InterruptedException e) {} System.out.println(name+", 你是第"+num+"个使用timer的线程"); //} } }
需要注意的是:
1.两个线程不可同时执行一个类中被声明了synchronized的方法;
2.当一个线程正在访问一个类的其中一个被声明为synchronized方法的方法的时候,其他线程可以访问该类中没有声明为synchronized的方法;
3.一个类中不可以同时有多个被声明为synchronized的方法被执行(一个对象只有一个互斥锁,需要等待另外一个线程释放互斥锁后才可以执行)。
Wait和sleep的区别:
Wait时,别的线程可以访问锁定的对象(调用wait的方法的时候必须锁定该对象)。
Sleep时,别的线程不可以访问锁定对象。
死锁的例子:
public class TestDeadLock implements Runnable { public int flag = 1; static Object o1 = new Object(), o2 = new Object(); public void run() { System.out.println("flag=" + flag); if(flag == 1) { synchronized(o1) { try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } synchronized(o2) { System.out.println("1"); } } } if(flag == 0) { synchronized(o2) { try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } synchronized(o1) { System.out.println("0"); } } } } public static void main(String[] args) { TestDeadLock td1 = new TestDeadLock(); TestDeadLock td2 = new TestDeadLock(); td1.flag = 1; td2.flag = 0; Thread t1 = new Thread(td1); Thread t2 = new Thread(td2); t1.start(); t2.start(); } }