线程小结

来源:互联网 发布:宁波网络推广公司 编辑:程序博客网 时间:2024/05/22 10:30

1.   理解程序、进程、线程的概念

程序:静态的代码

进程:执行中的程序

线程:可以理解为进程的多条执行线索,每条线索又对应着各自独立的生命周期

2.   如何创建java程序的线程(重点)

1)       创建多线程的方式一:继承方式实现

package cn.km.thread01;class SubThread1 extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {if(i%2 == 0){System.out.println(Thread.currentThread().getName() + ":" + i);}}}public SubThread1(String name) {super(name);}}class SubThread2 extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {if(i%2 != 0){System.out.println(Thread.currentThread().getName() + ":" + i);}}}public SubThread2(String name) {super(name);}}public class TestThread01 {public static void main(String[] args) {SubThread1 st1 = new SubThread1("线程1");SubThread2 st2 = new SubThread2("线程2");st1.setPriority(Thread.MIN_PRIORITY);//设置优先级最低 1st2.setPriority(Thread.MAX_PRIORITY); //设置优先级最高 10st1.start();st2.start();}}

2)       多线程实现方式二:实现方式实现

创建一个实现了Runnable接口的类

实现接口的抽象方法

创建一个Runnable接口实现类的对象

将此对象作为形参传递给Thread类的构造器中,创建Thread类的对象,此对象即为一个线程

启动线程,执行Thread的start方法。

package cn.km.thread01;class subThread implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {if(i%2 != 0){System.out.println(Thread.currentThread().getName() + ":" + i);}}}}public class TestThread02 {public static void main(String[] args) {subThread target = new subThread();Thread thread1 = new Thread(target);Thread thread2 = new Thread(target);Thread thread3 = new Thread(target);thread1.setName("线程1");thread2.setName("线程2");thread3.setName("线程3");thread1.start();thread2.start();thread3.start();}}

3)       两者的异同

联系:Thread也实现了Runnable,

哪个方式好??实现方式优于继承方式

Why?  避免了java单继承的局限性

                   如果多个线程要操作同一份资源(或数据),更适合使用实现的方式

4)       模拟窗口售票代码(此代码有隐患)


package cn.km.thread01;class Window implements Runnable{int ticket = 100;@Overridepublic void run() {while(true){if(ticket > 0){//try {//Thread.currentThread().sleep(10);//} catch (InterruptedException e) {//// TODO Auto-generated catch block//e.printStackTrace();//}System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket--);}else{break;}}}}public class TestThread03 {public static void main(String[] args) {Window target = new Window();Thread thread1 = new Thread(target);Thread thread2 = new Thread(target);Thread thread3 = new Thread(target);thread1.setName("窗口1");thread2.setName("窗口2");thread3.setName("窗口3");thread1.start();thread2.start();thread3.start();}}

3.   线程的常用方法


1)       start(),启动线程并执行相应的run()方法

2)       run():子线程要执行的代码放入run()方法中

3)       currentThread() :静态的,调取当前的线程

4)       getName() :获取此线程的名称

5)       setName() :设置此线程的名称

6)       yield() :调用此方法的线程释放当前CPU的执行权

7)       join():在A线程中调用B线程的join()方法,表示当执行到此方法,A线程停止执行,直至B线程执行完毕,A线程再接着join()之后的代码执行

8)       isAlive() :判断当前线程是否还存活

9)       sleep(long l) :显示的让当前线程睡眠1毫秒

10)    线程的通信:wait()  notify()  notifyAll()

11)    设置线程的优先级

getPriority() :返回线程优先值

setPriority(intnewPriority) :改变线程的优先级【MAX_PRIORITY=10;MIN_PRIORITY =1;NORM_PRIORITY=5;】


4.   线程的生命周期



5.   线程的同步机制(重点、难点)

package cn.km.thread02;/** * 此程序存在线程安全问题:打印车票时,会出现重票、错票 * 1.线程安全问题存在的原因 *由于一个线程再操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在了安全问题 * 2.如何解决线程安全问题 * 必须让一个线程操作共享数据完毕后,其他线程才有机会参与共享数据的操作 * 3.java如何实现线程的安全:线程同步机制 * 方式一:同步代码块 * synchronized(同步监视器){ * //需要被同步的代码块(即为操作共享数据的代码) * } * 共享数据:多个线程共同操作的同一个数据(变量) * 同步监视器:由一个类的对象充当。哪个线程获取此监视器,谁就执行大括号中被同步的代码。俗称:锁 * 要求:所有线程必须公用一把锁 * 注意:在实现的方式中,考虑同步的话,可以使用this来充当锁。但是在继承的方式中,慎用this * 方式二:同步方法 */@SuppressWarnings("static-access")class Window implements Runnable{int ticket = 100;@Overridepublic void run() {while(true){synchronized (this) { //加锁if (ticket > 0) {try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket--);} else {break;}}}}}public class TestWindow01 {public static void main(String[] args) {Window target = new Window();Thread thread1 = new Thread(target);Thread thread2 = new Thread(target);Thread thread3 = new Thread(target);thread1.setName("窗口1");thread2.setName("窗口2");thread1.start();thread2.start();}}

此程序存在线程安全问题:打印车票时,会出现重票、错票

  1.线程安全问题存在的原因

        由于一个线程再操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在了安全问题

  2.如何解决线程安全问题

       必须让一个线程操作共享数据完毕后,其他线程才有机会参与共享数据的操作

 3.java如何实现线程的安全:线程同步机制

6.   线程的同步实现(重点、难点)

1)       同步代码块

synchronized(同步监视器){

           //需要被同步的代码块(即为操作共享数据的代码)

 }

共享数据:多个线程共同操作的同一个数据(变量)

同步监视器:由一个类的对象充当。哪个线程获取此监视器,谁就执行大括号中被同步的代码。俗称:锁

要求:所有线程必须公用一把锁

 注意:在实现的方式中,考虑同步的话,可以使用this来充当锁。但是在继承的方式中,慎用this


2)       同步方法

package cn.km.thread02;/** * 此程序存在线程安全问题:打印车票时,会出现重票、错票 * 1.线程安全问题存在的原因 *由于一个线程再操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在了安全问题 * 2.如何解决线程安全问题 * 必须让一个线程操作共享数据完毕后,其他线程才有机会参与共享数据的操作 * 3.java如何实现线程的安全:线程同步机制 * 方式一:同步代码块 * synchronized(同步监视器){ * //需要被同步的代码块(即为操作共享数据的代码) * } * 共享数据:多个线程共同操作的同一个数据(变量) * 同步监视器:由一个类的对象充当。哪个线程获取此监视器,谁就执行大括号中被同步的代码。俗称:锁 * 要求:所有线程必须公用一把锁 * 注意:在实现的方式中,考虑同步的话,可以使用this来充当锁。但是在继承的方式中,慎用this * 方式二:同步方法 */@SuppressWarnings("static-access")class Window3 implements Runnable{int ticket = 100;@Overridepublic void run() {while(true){sell();}}public synchronized void sell(){ //添加同步方法  if (ticket > 0) {try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket--);}}}public class TestWindow02 {public static void main(String[] args) {Window3 target = new Window3();Thread thread1 = new Thread(target);Thread thread2 = new Thread(target);thread1.setName("窗口1");thread2.setName("窗口2");thread1.start();thread2.start();}}


7.   线程的通信

Notify/notifyAll

Wait

注意:java.lang.Object提供的这三个方法只有在synchronized方法或代码块中才能使用,否则会有异常:java.lang.IllegalMonitorStateException



package cn.km.thread02;/** *  */@SuppressWarnings("static-access")class Window4 implements Runnable{int ticket = 100;@Overridepublic void run() {while(true){sell();}}public synchronized void sell(){ //添加同步方法  notify(); //唤醒线程if (ticket > 0) {try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket--);}//线程等待try {wait();} catch (InterruptedException e) {e.printStackTrace();}}}public class TestWindow03 {public static void main(String[] args) {Window4 target = new Window4();Thread thread1 = new Thread(target);Thread thread2 = new Thread(target);thread1.setName("窗口1");thread2.setName("窗口2");thread1.start();thread2.start();}}

8.   生产者和消费者问题

生产者(Productor)将产品交给店员(Clerk),消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品,如果生产者视图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。


package cn.km.thread03;/** * 生产者/消费者问题 生产者(Productor)将产品交给店员(Clerk), 消费者(Customer)从店员处取走产品, * 店员一次只能持有固定数量的产品,如果生产者视图生产更多的产品,店员会叫生产者停一下, 如果店中有空位放产品了再通知生产者继续生产; * 如果店中没有产品了,店员会告诉消费者等一下, 如果店中有产品了再通知消费者来取走产品。 *  *  * 分析: 1.是否涉及到多线程的问题? 是 生产者/消费者 2.是否涉及共享数据 ? 有 需要考虑线程安全 3.此共享数据为谁? 产品的数量 4.是否涉及到线程的通信呢? 存在生产者、消费者的通信; */class Clerk { // 店员int product;public synchronized void addProduct() {// 生产产品if (product >= 20) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}} else {product++;System.out.println(Thread.currentThread().getName() + ":生产了第" + product + "个产品");notifyAll();}}public synchronized void consumeProduct() { // 消费产品if (product <= 0) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}} else {System.out.println(Thread.currentThread().getName() + ":消费了第" + product + "个产品");product--;notifyAll();}}}class Producer implements Runnable {Clerk clerk;public Producer(Clerk clerk) {this.clerk = clerk;}public void run() {System.out.println("生产者开始生产产品");while (true) {try {Thread.currentThread().sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}clerk.addProduct();}};}class Consumer implements Runnable {Clerk clerk;public Consumer(Clerk clerk) {this.clerk = clerk;}public void run() {System.out.println("消费者开始消费产品");while (true) {try {Thread.currentThread().sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}clerk.consumeProduct();}};}public class ProductorCustomer {public static void main(String[] args) {Clerk clerk = new Clerk();Producer producer1 = new Producer(clerk);Consumer consumer1 = new Consumer(clerk);Thread t1 = new Thread(producer1);Thread t2 = new Thread(consumer1);Thread t3 = new Thread(producer1);t1.setName("生产者1");t2.setName("消费者");t3.setName("生产者2");t1.start();t2.start();t3.start();}}


9.   面试题总结

1)     wait  和 sleep 方法的区别

1)这两个方法来自不同的类分别是   sleep来自Thread类,和wait来自Object

sleep是Thread的静态方法,谁调用谁就去睡觉,

即使在a线程里调用b线程的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep????????

2)锁:最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他的线程可以使用同步控制块或方法

sleep不出让系统资源

wait是进入线程等待池等待,出让系统资源,其他线程可以占用cpu。

wait不加时间限制,要等待其他的notify/notifyAll唤醒等待池中所有的线程,才会进入就绪队列等待分配系统资源。。

sleep(milliseconds)可以使用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。

3)使用范围:wait、notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。

synchronized(){

         notify();//notifyAll();

         //wait();

}


1)     创建线程的3种方式

继承Thread

实现Runnable接口

实现Callable接口

package cn.km.thread05;import java.util.concurrent.Callable;public class MyCallable implements Callable{@Overridepublic Object call() throws Exception {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + ":"+i);}return null;}}
public static void main(String[] args) {ExecutorService pool = Executors.newFixedThreadPool(2);pool.submit(new MyCallable());pool.submit(new MyCallable());}


2)     什么是线程安全
3)     Runnable和Callable接口的区别
4)     synchronized、lock、ReentrantLock、ReadWriteLock
5)     介绍下CAS(无锁技术)
6)     什么是ThreadLocal
7)     创建线程池的4中方式
8)     ThreadPoolExecutor的内部工作原理

9)     分布式环境下,怎么保持线程安全


正在整理中。。。