1.run 方法与start方法的区别
(1)start方法:开启线程并执行run方法的代码
(2)run方法:仅仅是对象调用方法,而线程创建了,并没有运行。//单线程
例如:下面的代码
1 public class MyThread extends Thread{ 2 3 private String name; 4 public MyThread(String name) { 5 this.name = name; 6 } 7 public void run() { 8 for(int i =0 ; i<5; i++){ 9 System.out.println(name+"====="+i); 10 } 11 } 12 13 14 public static void main(String[] args) {//里面的代码并没有实现多线程,都是main线程执行 15 MyThread t = new MyThread("one"); 16 MyThread t2 = new MyThread("two"); 17 t.run(); //t线程没有启动,是main线程跳到 MyThread的run方法中执行其中的代码,下同 18 t2.run(); 19 for(int i =0 ; i<5; i++){ 20 System.out.println("main====="+i); 21 } 22 23 } 24 }
输出结果:
one=====0
one=====1
one=====2
one=====3
one=====4
two=====0
two=====1
two=====2
two=====3
two=====4
main=====0
main=====1
main=====2
main=====3
main=====4
结论:如果需要实现多线程运行, 把t.run();t2.run();改为t.start();t2.start();
2.线程的运行状态的转换图
3.实现线程的两种方式:
(1)继承Thread类,重写run方法
(2)实现Runnable接口,实现run方法
4.两种方式的区别:
(1)实现Runnable接口的方式,可以避免java单继承的局限性,建议使用这种方式,而且这种方式可以资源共享(售票例子)
(2)继承Thread:线程代码封装在Thread子类的run代码中
实现接口方式:线程代码封装在实现Runnable接口的子类的run方法中
5.线程同步方式有两种
(1)同步代码块,方式:synchronized(对象){需要同步的代码}
(2)同步函数:在非静态函数声明前面加入synchronized,这种方式使用的锁是this。如果在静态函数[static]面前用synchronized修改,同步的对象是:对函数的类名.class.即使用的锁是该方法所在类的字节码文件对象
PS:同步的方式都是对对象加锁,同步函数的方式是对当前对象加锁,同步代码块可以指定特定的对象加锁
6.如何分析线程安全的代码
(1)明确哪些代码是多线程运行代码
(2)明确共享数据
(3)明确多线程运行代码中哪些语句 是操作共享数据的
7.线程通讯:通过wait();notify();notifyAll();方法来实现
(1)都使用在同步中,因为要对持有监视器(锁)的线程操作
所以要使用在同步中,因为只有同步才具有锁
(2)为什么这些操作线程的方法要定义Object类中?
因为这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁,
只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。
不可以对不同锁中的线程进行唤醒,也就是说,等待和唤醒必须是同一个锁
而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中
8.生产者消费者问题
(1)共享资源代码
/** * 共享资源 * @author lisheng90777 * */ public class Resource { private int index = 1;//商品编号 private String name = null;//商品名称 private boolean flag = false;//同步标志位 public synchronized void set(String name){ while(flag){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.name = name+""+index++; System.out.println("生产者生产了"+this.name); flag =true; this.notifyAll();//如果只有一个生产者一个消费者,可以用notify,但是是多个消费者与多个生产者,必须到notifyAll(); } public synchronized void out(){ while(!flag){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费者消费----"+this.name); flag =false; this.notifyAll(); } }
(2)生产者代码
/** * 生产者 * @author lisheng90777 * */ public class Producer implements Runnable{ private Resource res; public Producer(Resource res) { this.res = res; } @Override public void run() { while(true){ res.set("土豆 "); } } }
(3)消费者代码
/** * 消费者 * @author lisheng90777 * */ public class Consumer implements Runnable{ private Resource res; public Consumer(Resource res) { this.res = res; } @Override public void run() { while(true){ res.out(); } } }
(4)main函数测试代码
public class ProducerConsumerDemo {
public static void main(String[] args) {
Resource res = new Resource();//共享资源
new Thread(new Producer(res)).start();//生产者线程1
new Thread(new Producer(res)).start();//生产者线程2
new Thread(new Consumer(res)).start();//消费者线程1
new Thread(new Consumer(res)).start();//消费者线程2
}
}
(5)测试结果【部分结果】
生产者生产了土豆 47110
消费者消费----土豆 47110
生产者生产了土豆 47111
消费者消费----土豆 47111
生产者生产了土豆 47112
消费者消费----土豆 47112
生产者生产了土豆 47113
消费者消费----土豆 47113
生产者生产了土豆 47114
消费者消费----土豆 47114
生产者生产了土豆 47115
消费者消费----土豆 47115
9.生产者消费者问题【升级版,利用JDK1.5Lock接口】
(1)只是改变共享资源的类Resource
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 共享资源:这个方式可以实现只是唤醒对方等待的线程,不需要把本方等待的线程也唤醒 * JDK1.5或者以上的版本 * @author lisheng90777 * */ public class Resource { private int index = 1;//商品编号 private String name = null;//商品名称 private boolean flag = false;//同步标志位 private Lock lock = new ReentrantLock(); private Condition condition_producer = lock.newCondition();//生产者线程操作对象 private Condition condition_consumer = lock.newCondition();//消费者线程操作对象 public void set(String name){ lock.lock();//加锁 try{ while(flag){ try { condition_producer.await();//生产者线程等待 } catch (InterruptedException e) { e.printStackTrace(); } } this.name = name+""+index++; System.out.println("生产者生产了"+this.name); flag =true; condition_consumer.signal();//唤醒消费者队列的第一个 } finally{ lock.unlock(); } } public void out(){ lock.lock(); try{ while(!flag){ try { condition_consumer.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费者消费----"+this.name); flag =false; condition_producer.signal();//唤醒生产者线程队列的第一个 }finally{ lock.unlock();//必须有try ,finally方式,目的是:如果try里面代码报错,finally里面一定执行,也就是一定需要释放锁 } } }
10.终止线程的interrupt方法:强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。
(1)线程类
public class StopThread implements Runnable{ private boolean flag = true; public synchronized void run() { while(flag){ try{ wait(); }catch(InterruptedException e){ System.out.println(Thread.currentThread().getName()+".....InterruptedException"); flag = false;//利用标志来控制线程是否结束 } System.out.println(Thread.currentThread().getName()+".....run"); } } public void changeFlag(){ flag = false; } }
(2)测试
1 public class StopThreadDemo { 2 3 public static void main(String[] args) { 4 StopThread st = new StopThread(); 5 Thread t1 = new Thread(st); 6 Thread t2 = new Thread(st); 7 t1.start(); 8 t2.start(); 9 int num =0; 10 while(true){ 11 if(num++ == 10){ 12 //st.changeFlag();//通过改变标志来结束线程,弊端:如果线程出于中断状态时,例如线程调用wait()方法 13 t1.interrupt();//强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。 14 t2.interrupt(); 15 break; 16 } 17 System.out.println(Thread.currentThread().getName()+"-----------"+num); 18 } 19 System.out.println("======over"); 20 } 21 }
(3)结果:
main-----------1 main-----------2 main-----------3 main-----------4 main-----------5 main-----------6 main-----------7 main-----------8 main-----------9 main-----------10 ======over Thread-1.....InterruptedException Thread-1.....run Thread-0.....InterruptedException Thread-0.....run
11.守护线程:在上面代码StopThreadDemo类中 ,在t1.start();前面加上t1.setDaemon(true);t2.setDaemon(true);,这两句语句表示:这个两个线程是守护线程,属于后台线程,当其他线程都运行完后,这两个后台线程就会自动终止,java虚拟机退出。注意:这个两个方法必须在启动线程前调用,也就是在start方法前调用 。
12.join方法:
(1)当A线程执行到了B线程的join方法时,A线程就会等待,等待B线程执行完,A才会执行
(2)join可以用来临时加入线程执行
(3)例子:
public class JoinMethod implements Runnable{ public void run() { for(int i = 0; i<5; i++){ System.out.println(Thread.currentThread().getName()+"线程正在执行-------"+i); } } }
public class JoinDemo { public static void main(String[] args) { JoinMethod sm = new JoinMethod(); Thread t1 = new Thread(sm); Thread t2 = new Thread(sm); t1.start(); try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); for(int i = 0 ; i<5; i++){ System.out.println("主线程main方法正在执行------------"+i); } System.out.println("main方法结束"); } }
Thread-0线程正在执行-------0 Thread-0线程正在执行-------1 Thread-0线程正在执行-------2 Thread-0线程正在执行-------3 Thread-0线程正在执行-------4 主线程main方法正在执行------------0 主线程main方法正在执行------------1 主线程main方法正在执行------------2 主线程main方法正在执行------------3 主线程main方法正在执行------------4 main方法结束 Thread-1线程正在执行-------0 Thread-1线程正在执行-------1 Thread-1线程正在执行-------2 Thread-1线程正在执行-------3 Thread-1线程正在执行-------4
13. yield()方法,例如:Thread.yield();暂停当前正在执行的线程对象,执行其他线程。