1.线程的基本概念
线程是一个程序里面不同的执行路径
在同一个时间点,一个cpu只能有一个线程在执行。
2.线程的创建与启动
第一种更好,因为一个类只能集成一个父类,而可以实现很多接口
第一种
定义线程实现Runnable接口
package com.anker.thread;
public class TestThread1 {
public static void main(String args[]) {
Runner1 r = new Runner1();
// r.run(); 直接调用等于方法调用,而非新启线程
Thread t = new Thread(r);
t.start();
for (int i = 0; i < 100; i++) {
System.out.println("Main Thread:" + i);
}
}
}
class Runner1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Runner1:" + i);
}
}
}
第二种
集成Thread
package com.anker.thread;
public class TestThread2 {
public static void main(String args[]) {
Runner2 r = new Runner2();
r.start();
for (int i = 0; i < 100; i++) {
System.out.println("Main Thread:" + i);
}
}
}
class Runner2 extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Runner1:" + i);
}
}
}
线程的状态:
start->就绪状态->运行状态->->终止
运行状态中会存在以下状态:
阻塞状态:sychronized同步块,需要等待对象锁或类锁
waiting: 调用object.wait,将当前线程处于等待状态,并将对象锁释放,等待notify或者notifyAll()唤醒。
注意:使用notify,在众多等待同一个锁的任务中只有一个被唤醒,而使用notifyAll唤醒的是等待同一个锁的所有任务。
注意是等待同一个锁。
sleep:Thread.sleep(),将当前线程处于sleep状态,但仍持有线程资源。
interrupt: 外部通过interrupt方法,将从sleep状态中唤醒。
线程的方法:
isAlive:线程是否终止
getPriority(): 获得线程的优先级
setPriority(): 设置线程的优先级(每个线程有自己的优先级,优先级越高的获得的cpu时间越多)
Thread.sleep(): 将当前线程睡眠指定毫秒数,但仍然持有线程资源
Thread.interrupt(); //中断线程, 此方法适用于将线程从sleep状态中唤醒。
public boolean Thread.isInterrupted() //判断是否中断
public static boolean Thread.interrupted() //判断是否中断,并清除当前中断状态。
join(): 调用某线程的该方法,将当前线程与该线程"合并",即等待该线程结束,再
恢复当前线程的运行
join的本质是
while(isAlive())
{
wait(0);
}
待线程执行完毕后,系统会调用notifyAll()。因此不建议在Thread实例中使用wait和notify()方法。有可能影响到API的调用。
yield(): 让出cpu,当前线程进入就绪队列等待调度(只是下一次的执行机会让给其他线程)
wait(): 当前线程进入对象的wait pool,与sleep不同,sleep在休眠过程的时候仍然持有锁,而wait会释放锁,
直到其他线程唤醒。
notify()/notifyAll():唤醒对象的wait pool中的一个/所有等待线程
挂起(suspend) 和继续执行(resume)线程,不建议使用此两个函数
--suspend 不会释放锁
--如果加锁在resume()之前,则发生死锁。
Thread.stop()不推荐使用,它会释所有monitor。
重新的方法不能抛出跟父类方法不同的异常
3.线程的调度和优先级
java提供一个线程调度器来监控程序中启动进入就绪状态的所有线程,
线程调度器按照线程的优先级决定调度那个进程来执行(优先级高的获得更多的cpu执行时间)
线程的优先级用数字来表示,范围从1到10,一个线程的缺省优先级是5
Thread.MIN_PRIORITY=1
Thread.MAX_PRIORITY=10
Thread.MNORM_PRIORITY=5
使用下述方法获得或设置对象的优先级
int getPriority();
void setPriority(int newPriority);
--Interrupt中断线程示例
--Interrupt方法是中断线程的一种优雅方式
public class Test implements Runnable{
public static void main(String args[]) {
Test r = new Test();
Thread t1 = new Thread(r);
t1.start();
for(int j=0;j<100;j++){
System.out.println("j:"+j);
}
t1.interrupt();
}
// public void run() {
// while(true){
// if(Thread.currentThread().isInterrupted()){ //检测是否中断
// System.out.println("thead is interrupt");
// break;
// }
// try {
// System.out.println("sleep");
// Thread.sleep(100000);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// System.out.println("interrupted when Sleep");
Thread.currentThread().interrupt();//设置中断状态,抛出异常后会自动清除中断标记位
// }
// }
//
// }
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {//判断当前线程是否发生中断,若是则break;
System.out.println("thead is interrupt");
break;
}
}
}
--Join示例
//结果:打印完i之后,再打印j
//join方式是将线程合并到主线程,待线程之后完之后,才会继续执行主线程的逻辑。
public static void main(String args[]) {
Test1 r = new Test1();
Thread t1 = new Thread(r);
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int j=0;j<100;j++){
System.out.println("j:"+j);
}
}
public void run() {
for(int i=0;i<100;i++){
System.out.println("i:"+i);
}
}
线程同步
所有同步的状态都要考虑加synchronized方法
在Java语言中,引入对象互斥锁的概念,保证共享数据操作的完整性,每个对象对应于
一个可称为"互斥锁"的标记,这个标记保证在任一时刻,只能有一个线程访问该对象。
当对象调用其任意synchronized方法的时候,此对象都被加锁,
这时该对象上的其他synchronized方法只有等到前一个方法调用完毕并释放了锁之后才被调用。
synchronized(xxx),指锁定xxx对象,只能有一个线程访问xxx对象,不相同的对象,是可以同步访问。
当关键字synchronized修饰方法时,是指锁定当前调用对象,等同于synchronized(this){}
–1. 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁。
public void run() {
for(int j=0;j<10000000;j++){
synchronized(instance){
i++;
}
}
–2. 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。等同于方法1的synchronized(this){...代码块..}
public synchronized void increase(){
i++;
}
–3. 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁,即使不同的对象访问此段代码,也要依次访问。
public static synchronized void increase(){
i++;
}
package com.anker.thread;
public class TT implements Runnable{
int b = 100;
public synchronized void m1() throws Exception {
System.out.println("执行m1方法");
b = 1000;
Thread.sleep(5000);
System.out.println("b= "+b);
}
public synchronized void m2() throws Exception{
System.out.println("执行m2方法");
Thread.sleep(2500);
b=2000;
System.out.println("b:"+b);
}
@Override
public void run() {
try {
System.out.println("执行run方法");
m1();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String args[]) throws Exception{
TT tt = new TT();
Thread t = new Thread(tt);
t.start();
tt.m2();
System.out.println(tt.b);
}
}
--在static方法上增加synchronized,即意味着持有类锁,不同的对象仍要依次访问。
//结果为20000000
public class AccountingSyncClass implements Runnable{
static int i=0;
public static synchronized void increase(){
i++;
}
public void run() {
for(int j=0;j<10000000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new AccountingSyncClass());
Thread t2=new Thread(new AccountingSyncClass());
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i);
}
}
// wait() 必须在synchronized 函数或者代码块里面
// wait()会让已经获得synchronized 函数或者代码块控制权的Thread暂时休息,并且丧失控制权
// 这个时候,由于该线程丧失控制权并且进入等待,其他线程就能取得控制权,并且在适当情况下调用notifyAll()来唤醒wait()的线程。
// 需要注意的是,被唤醒的线程由于已经丧失了控制权,所以需要等待唤醒它的线程结束操作,从而才能重新获得控制权。
// 所以wait()的确是马上让当前线程丧失控制权,其他的线程可以乘虚而入。
// 所以wait()的使用,必须存在2个以上线程,而且必须在不同的条件下唤醒wait()中的线程。
// 当存在多个等待线程时, notify是随机选择等待的线程进行唤醒。
//结果打印顺序
T1 start!
T1 wait for object
T2 start! notify one thread
T2 end!
T1 end!
public class TestWait {
final static Object object = new Object();
public static class T1 extends Thread{
public void run()
{
synchronized (object) {
System.out.println(System.currentTimeMillis()+":T1 start! ");
try {
System.out.println(System.currentTimeMillis()+":T1 wait for object ");
object.wait(); //如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。这里需要采用锁对象的的对象
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+":T1 end!");
}
}
}
public static class T2 extends Thread{
public void run()
{
synchronized (object) {
System.out.println(System.currentTimeMillis()+":T2 start! notify one thread");
object.notify();
System.out.println(System.currentTimeMillis()+":T2 end!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
}
}
}
public static void main(String[] args) {
Thread t1 = new T1() ;
Thread t2 = new T2() ;
// Thread t1_1 = new T1() ;
// t1_1.start();
t1.start();
t2.start();
}
}
消费者与生产者的故事
package com.anker.thread;
public class ProducerConsumer {
public static void main(String args[]){
SynStack ss = new SynStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
new Thread(p).start();
new Thread(c).start();
}
}
class WoTou{
int id;
WoTou(int id){
this.id = id;
}
public String toString(){
return "WoTou:"+id;
}
}
class SynStack{
int index = 0;
WoTou[] arrWT = new WoTou[6];
public synchronized void push(WoTou wt){
while(index == arrWT.length){//使用while会确保线程被打断时,仍然要执行判断
try {
System.out.println("push wait方法被调用");
this.wait();//只能在同步控制方法或同步块中调用wait()、notify()和notifyAll()。如果在非同步的方法里调用这些方法抛出异常
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notify();
arrWT[index] = wt;
System.out.println("push WoTou index:"+index);
index ++;
}
public synchronized WoTou pop(){
while(index == 0){
try {
System.out.println("pop wait方法被调用");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notify();
index --;
System.out.println("pop WoTou index:"+index);
return arrWT[index];
}
}
class Producer implements Runnable{
SynStack ss = null;
Producer(SynStack ss){
this.ss = ss;
}
@Override
public void run() {
for(int i=0;i<20;i++){
WoTou wt= new WoTou(i);
ss.push(wt);
// System.out.println("WtTou is produced:"+wt);
try{
Thread.sleep((int)(Math.random() * 1000));
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable{
SynStack ss = null;
Consumer(SynStack ss){
this.ss = ss;
}
@Override
public void run() {
for(int i=0;i<20;i++){
WoTou wt= ss.pop();
// System.out.println("WtTou is consumer:"+wt);
try{
Thread.sleep((int)(Math.random() * 1000));
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}