- 浏览: 3193 次
文章分类
最新评论
一、多线程概述
1、 进程
是一个正在执行的程序。
每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
2、线程
就是进程中的一个独立的控制单元。线程在控制着进程的执行。只要进程中有一个线程在执行,进程就不会结束。
一个进程中至少有一个线程。
3、多线程
在java虚拟机启动的时候会有一个java.exe的执行程序,也就是一个进程。该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。JVM启动除了执行一个主线程,还有负责垃圾回收机制的线程。这种在一个进程中有多个线程执行的方式,就叫做多线程。
4、多线程存在的意义
多线程的出现能让程序产生同时运行效果。可以提高程序执行效率。
例如:在java.exe进程执行主线程时,如果程序代码特别多,在堆内存中产生了很多对象,而同时对象调用完后,就成了垃圾。如果垃圾过多就有可能是堆内存出现内存不足的现象,只是如果只有一个线程工作的话,程序的执行将会很低效。而如果有另一个线程帮助处理的话,如垃圾回收机制线程来帮助回收垃圾的话,程序的运行将变得更有效率。
5、计算机CPU的运行原理
我们电脑上有很多的程序在同时进行,就好像cpu在同时处理这所有程序一样。但是,在一个时刻,单核的cpu只能运行一个程序。而我们看到的同时运行效果,只是cpu在多个进程间做着快速切换动作。
而cpu执行哪个程序,是毫无规律性的。这也是多线程的一个特性:随机性。哪个线程被cpu执行,或者说抢到了cpu的执行权,哪个线程就执行。而cpu不会只执行一个,当执行一个一会后,又会去执行另一个,或者说另一个抢走了cpu的执行权。至于究竟是怎么样执行的,只能由cpu决定。
二、创建线程的方式
创建线程共有两种方式:
1、 继承方式
继承Thread类,重写run方法。
覆盖run方法的原因:
Thread类用于描述线程。Thread类中的run方法,用于存储线程要运行的代码。
2、 实现Runnable接口
如果类本来就继承了其他父类,那么就无法通过Thread类来创建线程了。这样就有了第二种创建线程的方式:实现Runnable接口,并重写其中run方法的方式。
程序示例:
三、线程的状态切换
(一)线程有新建(New)→就绪(Runnable)→运行(Running)→阻塞(Blocked)→死亡(Dead)五种状态.
0. 启动线程应该调用start()方法, 如果调用run()方法则会使该线程一直处于Running状态, 不能和其他线程并发执行.
1. 线程对象调用start()之后, 该线程处于Runnable状态, 但是并没有运行, 什么时候运行取决于JVM里线程调度器的调度.
2. 如果希望调用子线程的start()方法后线程立即执行, 可以用Thread.sleep(1)来让当前运行的线程(主线程)睡眠1毫秒. (因为这1毫秒内CPU不会空闲, 它去执行另一个Runnable状态的线程.)
3 运行和阻塞状态
现代桌面和服务器OS都采用抢占式调度策略, 但有的小型设备如手机可能采用"协作式调度策略"--只有当一个线程调用了它的sleep()或yield()方法后才会放弃所占用的资源(即该线程必须主动放弃).
(二)线程状态可能多次在Running和Blocked之间切换.
1.进入阻塞状态的条件和解除方法:
1)sleep()
2)线程调用了阻塞式的IO方法, 在该方法返回之前, 该线程被阻塞.
3)线程试图获得一个"同步监视器", 但该同步监视器被其他线程所持有. (成功获得同步监视器后解除阻塞.)
4)线程在等待某个通知(notify).
5)程序调用suspend()方法将该线程挂起. (调用resume()方法恢复.)
2. 被阻塞的线程的阻塞解除后, 进入就绪状态(Runnable)不是直接进入运行状态(Running).
3. 通常运行状态和就绪状态不受程序控制, 但yield()方法可以让运行状态的线程进入就绪状态.
(三)线程死亡
线程结束方式有三种, 结束后就是Dead状态.
1.run()或call()方法执行完.
2.线程抛出一个未捕获的Exception或Error.
3.直接调用该线程的stop()方法.(容易导致死锁, 不推荐.)
四、线程安全问题
1、导致安全问题的出现的原因:
当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没用执行完,另一个线程参与进来执行。导致共享数据的错误。
简单的说就两点:
a、多个线程访问出现延迟。
b、线程随机性。
2、解决办法——同步
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
在java中对于多线程的安全问题提供了专业的解决方式——synchronized(同步)
这里也有两种解决方式,一种是同步代码块,还有就是同步函数。都是利用关键字synchronized来实现。
a、同步代码块
用法:
synchronized(对象)
{需要被同步的代码}
同步可以解决安全问题的根本原因就在那个对象上。其中对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
b,同步函数
格式:
在函数上加上synchronized修饰符即可。
那么同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this。
拿同步代码块的示例:
3、同步的前提
a,必须要有两个或者两个以上的线程。
b,必须是多个线程使用同一个锁。
4、同步的利弊
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
5、如何寻找多线程中的安全问题
a,明确哪些代码是多线程运行代码。
b,明确共享数据。
c,明确多线程运行代码中哪些语句是操作共享数据的。
五、静态函数的同步方式
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不在是this。因为静态方法中也不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。如:
类名.class 该对象的类型是Class
这就是静态函数所使用的锁。而静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class
六、死锁
当同步中嵌套同步时,就有可能出现死锁现象。
七、线程间通信
其实就是多个线程在操作同一个资源,但是操作的动作不同。
1、使用同步操作同一资源的示例:
几个小问题:
1)wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
a,这些方法存在与同步中。
b,使用这些方法时必须要标识所属的同步的锁。同一个锁上wait的线程,只可以被同一个锁上的notify唤醒。
c,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
2)wait(),sleep()有什么区别?
wait():释放cpu执行权,释放锁。
sleep():释放cpu执行权,不释放锁。
3)为甚么要定义notifyAll?
因为在需要唤醒对方线程时。如果只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。比如把上面例子该一下,创建多个输入多个输出线程同步执行,将会出错。
上面这个例子,因为在set、get方法中对flag的判断采用的是if,会导致生产者连续生产多个产品,后面的产品覆盖了前面的产品,消费者消费时只读到了最后一个产品。或者生产者生产了一个产品,而消费者反复消费了多次。
而当我们把if改为while后。执行完一次set或get后,在需要唤醒对方线程时,用notify,容易出现只唤醒本方线程的情况。会导致所有线程都进入wait()。
2、JDK1.5中提供了多线程升级解决方案。
Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。 Condition对象可以通过Lock锁进行获取,并支持多个相关的Condition对象。
八、停止线程
在JDK 1.5版本之前,有stop停止线程的方法,但升级之后,此方法已经过时。
那么现在我们该如果停止线程呢?
只有一种办法,那就是让run方法结束。
1、开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
如:run方法中有如下代码,设置一个flag标记。
那么只要在主函数或者其他线程中,在该线程执行一段时间后,将标记flag赋值false,该run方法就会结束,线程也就停止了。
2、上面的1方法可以解决一般情况,但是有一种特殊情况:就是当线程处于冻结状态。就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法interrupt();
如:
九、什么时候写多线程?
当某些代码需要同时被执行时,就用单独的线程进行封装。
示例:
扩展小知识:
1、join方法
当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。
2、setPriority()方法用来设置优先级
MAX_PRIORITY 最高优先级10
MIN_PRIORITY 最低优先级1
NORM_PRIORITY 分配给线程的默认优先级
3、yield()方法可以暂停当前线程,让其他线程执行。
1、 进程
是一个正在执行的程序。
每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
2、线程
就是进程中的一个独立的控制单元。线程在控制着进程的执行。只要进程中有一个线程在执行,进程就不会结束。
一个进程中至少有一个线程。
3、多线程
在java虚拟机启动的时候会有一个java.exe的执行程序,也就是一个进程。该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。JVM启动除了执行一个主线程,还有负责垃圾回收机制的线程。这种在一个进程中有多个线程执行的方式,就叫做多线程。
4、多线程存在的意义
多线程的出现能让程序产生同时运行效果。可以提高程序执行效率。
例如:在java.exe进程执行主线程时,如果程序代码特别多,在堆内存中产生了很多对象,而同时对象调用完后,就成了垃圾。如果垃圾过多就有可能是堆内存出现内存不足的现象,只是如果只有一个线程工作的话,程序的执行将会很低效。而如果有另一个线程帮助处理的话,如垃圾回收机制线程来帮助回收垃圾的话,程序的运行将变得更有效率。
5、计算机CPU的运行原理
我们电脑上有很多的程序在同时进行,就好像cpu在同时处理这所有程序一样。但是,在一个时刻,单核的cpu只能运行一个程序。而我们看到的同时运行效果,只是cpu在多个进程间做着快速切换动作。
而cpu执行哪个程序,是毫无规律性的。这也是多线程的一个特性:随机性。哪个线程被cpu执行,或者说抢到了cpu的执行权,哪个线程就执行。而cpu不会只执行一个,当执行一个一会后,又会去执行另一个,或者说另一个抢走了cpu的执行权。至于究竟是怎么样执行的,只能由cpu决定。
二、创建线程的方式
创建线程共有两种方式:
1、 继承方式
继承Thread类,重写run方法。
覆盖run方法的原因:
Thread类用于描述线程。Thread类中的run方法,用于存储线程要运行的代码。
public class TestThread { public static void main(String[] args) { new Test("one").start(); //3.创建定义类的实例对象。相当于创建一个线程。4.调用start方法 new Test("two").start(); for (int i = 0; i < 60; i++) { System.out.println("main" + " " + i); } } } class Test extends Thread {//1.继承 Test(String name) { super(name); } public void run() {//2.重写run方法 for (int i = 0; i < 60; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } } }
2、 实现Runnable接口
如果类本来就继承了其他父类,那么就无法通过Thread类来创建线程了。这样就有了第二种创建线程的方式:实现Runnable接口,并重写其中run方法的方式。
程序示例:
/* 需求:简单的卖票程序。 多个窗口卖票。 */ class Ticket implements Runnable { private int tick = 100; public void run() { while(true) { if(tick>0) { //显示线程名及余票数 System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); } } } } class TicketDemo { public static void main(String[] args) { //创建Runnable接口子类的实例对象 Ticket t = new Ticket(); //有多个窗口在同时卖票,这里用四个线程表示 Thread t1 = new Thread(t);//创建了一个线程 Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); t1.start();//启动线程 t2.start(); t3.start(); t4.start(); } }
三、线程的状态切换
(一)线程有新建(New)→就绪(Runnable)→运行(Running)→阻塞(Blocked)→死亡(Dead)五种状态.
0. 启动线程应该调用start()方法, 如果调用run()方法则会使该线程一直处于Running状态, 不能和其他线程并发执行.
1. 线程对象调用start()之后, 该线程处于Runnable状态, 但是并没有运行, 什么时候运行取决于JVM里线程调度器的调度.
2. 如果希望调用子线程的start()方法后线程立即执行, 可以用Thread.sleep(1)来让当前运行的线程(主线程)睡眠1毫秒. (因为这1毫秒内CPU不会空闲, 它去执行另一个Runnable状态的线程.)
3 运行和阻塞状态
现代桌面和服务器OS都采用抢占式调度策略, 但有的小型设备如手机可能采用"协作式调度策略"--只有当一个线程调用了它的sleep()或yield()方法后才会放弃所占用的资源(即该线程必须主动放弃).
(二)线程状态可能多次在Running和Blocked之间切换.
1.进入阻塞状态的条件和解除方法:
1)sleep()
2)线程调用了阻塞式的IO方法, 在该方法返回之前, 该线程被阻塞.
3)线程试图获得一个"同步监视器", 但该同步监视器被其他线程所持有. (成功获得同步监视器后解除阻塞.)
4)线程在等待某个通知(notify).
5)程序调用suspend()方法将该线程挂起. (调用resume()方法恢复.)
2. 被阻塞的线程的阻塞解除后, 进入就绪状态(Runnable)不是直接进入运行状态(Running).
3. 通常运行状态和就绪状态不受程序控制, 但yield()方法可以让运行状态的线程进入就绪状态.
(三)线程死亡
线程结束方式有三种, 结束后就是Dead状态.
1.run()或call()方法执行完.
2.线程抛出一个未捕获的Exception或Error.
3.直接调用该线程的stop()方法.(容易导致死锁, 不推荐.)
四、线程安全问题
1、导致安全问题的出现的原因:
当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没用执行完,另一个线程参与进来执行。导致共享数据的错误。
简单的说就两点:
a、多个线程访问出现延迟。
b、线程随机性。
2、解决办法——同步
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
在java中对于多线程的安全问题提供了专业的解决方式——synchronized(同步)
这里也有两种解决方式,一种是同步代码块,还有就是同步函数。都是利用关键字synchronized来实现。
a、同步代码块
用法:
synchronized(对象)
{需要被同步的代码}
同步可以解决安全问题的根本原因就在那个对象上。其中对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
/* 给卖票程序示例加上同步代码块。 */ class Ticket implements Runnable { private int tick=100; Object obj = new Object(); public void run() { while(true) { //给程序加同步,即锁 synchronized(obj) { if(tick>0) { try { //使用线程中的sleep方法,模拟线程出现的安全问题 //因为sleep方法有异常声明,所以这里要对其进行处理 Thread.sleep(10); } catch (Exception e) { } //显示线程名及余票数 System.out.println(Thread.currentThread().getName()+"..tick="+tick--); } } } } }
b,同步函数
格式:
在函数上加上synchronized修饰符即可。
那么同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this。
拿同步代码块的示例:
/*验证同步方法用的锁*/ public class TestSuo { public static void main(String[] args) { Ticket1 st = new Ticket1(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.start(); try { Thread.sleep(20); } catch (Exception e) { } st.flag = false; t2.start(); } } class Ticket1 implements Runnable { public boolean flag = true; private int num = 100; public void run() { if (flag) {// t1进同步代码块执行 while (true) { synchronized (this) {// 此处用this锁,则t1、t2两个线程并发读取操作num,不会导致打印出num=0的情况.假如换成别的锁,如obj,则会打印出num=0的情况。验证结论synchronized用的锁,是this if (num > 0) { try { Thread.sleep(20); } catch (Exception e) { } System.out.println("线程名: " + Thread.currentThread().getName() + "; 余票num:" + num--); } } } } else {// t2进同步方法执行 while (true) { show(); } } } synchronized void show() { if (num > 0) { try { Thread.sleep(20); } catch (Exception e) { } System.out.println("线程名: " + Thread.currentThread().getName() + "; 余票数量:" + num--); } } }
3、同步的前提
a,必须要有两个或者两个以上的线程。
b,必须是多个线程使用同一个锁。
4、同步的利弊
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
5、如何寻找多线程中的安全问题
a,明确哪些代码是多线程运行代码。
b,明确共享数据。
c,明确多线程运行代码中哪些语句是操作共享数据的。
五、静态函数的同步方式
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不在是this。因为静态方法中也不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。如:
类名.class 该对象的类型是Class
这就是静态函数所使用的锁。而静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class
/* 饿汉式 class Singleton{ private static final Singleton s = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return s; } } */ //懒汉式 class Singleton { private static Singleton s = null; private Singleton() { } public static Singleton getInstance() { if (s == null) {// 两次判断,避免了反复判断同步锁,提高效率 synchronized (Singleton.class) { if (s == null) {// 假如没有上一行的同步控制,多线程执行到此,线程A在此处阻塞,线程B进入此处,则A、B会在下一行代码分别创建两个实例 s = new Singleton(); } } } return s; } } public class SingletonDemo { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); } }
六、死锁
当同步中嵌套同步时,就有可能出现死锁现象。
public class DeadLockTest { public static void main(String[] args) { Thread t1 = new Thread(new DeadLock(true)); Thread t2 = new Thread(new DeadLock(false)); t1.start(); t2.start(); } } class DeadLock implements Runnable { private boolean flag;//通过flag控制两个线程分别进入run方法的不同语句块执行 DeadLock(boolean flag) { this.flag = flag; } public void run() { if (flag) { while (true) {//先等a锁,再等b锁 synchronized (MyLock.a) { System.out.println("if a "); synchronized (MyLock.b) { System.out.println("if b"); } } } } else { while (true) {//先等b锁,再等a锁 synchronized (MyLock.b) { System.out.println("else b "); synchronized (MyLock.a) { System.out.println("else a"); } } } } } } class MyLock { static Object a = new Object(); static Object b = new Object(); }
七、线程间通信
其实就是多个线程在操作同一个资源,但是操作的动作不同。
1、使用同步操作同一资源的示例:
public class InputOutput{ public static void main(String[] args){ Res r = new Res(); Input in = new Input(r); Output out = new Output(r); Thread t1 = new Thread(in); Thread t2 = new Thread(out); t1.start(); t2.start(); } } class Res{ String name; String sex; boolean flag; public synchronized void setInput(String name, String sex){ if(flag){//已经存入资源,等待取出 try{wait();}catch(Exception e){} } this.name=name; this.sex=sex; flag=true;//已经存入资源 notify();//唤醒取出线程 } public synchronized void getOutput(){ if(!flag){//没有数据,等待存入数据 try{wait();}catch(Exception e){} } System.out.println("name:"+name+" sex:"+sex);//取出数据 flag=false;//资源已取出 notify();//唤醒等待 } } class Input implements Runnable{ private Res r; Input(Res r){ this.r=r; } public void run(){ int x =0; while(true){ if(x == 0){ r.setInput("张三","男"); }else{ r.setInput("Linda","femail"); } x = (x+1)%2; } } } class Output implements Runnable{ private Res r ; Output(Res r){ this.r=r; } public void run(){ while(true){ r.getOutput(); } } }
几个小问题:
1)wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
a,这些方法存在与同步中。
b,使用这些方法时必须要标识所属的同步的锁。同一个锁上wait的线程,只可以被同一个锁上的notify唤醒。
c,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
2)wait(),sleep()有什么区别?
wait():释放cpu执行权,释放锁。
sleep():释放cpu执行权,不释放锁。
3)为甚么要定义notifyAll?
因为在需要唤醒对方线程时。如果只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。比如把上面例子该一下,创建多个输入多个输出线程同步执行,将会出错。
public class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); Producer p = new Producer(r); Consumer c = new Consumer(r); Thread t1 = new Thread(p); Thread t2 = new Thread(p); Thread t3 = new Thread(c); Thread t4 = new Thread(c); t1.start(); t2.start(); t3.start(); t4.start(); } } class Resource { private String name; private int count = 0; private boolean flag = false; // flag= false count= // t1 t2 public synchronized void set(String name) { if (flag) {// 改为while,一旦线程从阻塞状态回到就绪-->执行,则需要判断;只有当生产者线程就绪-->执行了,并判断flag为false了,才往下面执行,进行生产 try { wait(); } catch (Exception e) { } // t1(放弃执行资格) t2(放弃执行资格) } this.name = name; count++; System.out.println(Thread.currentThread().getName() + "Produce " + name + "-" + count); flag = true; notify();// notifyAll() } // t3 t4 public synchronized void get() { if (!flag) {// while 同set方法 try { wait(); } catch (Exception e) { } // t3() t4() } System.out.println(Thread.currentThread().getName() + "consume ---------------- " + name + count); flag = false; notify();// notifyAll } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while (true) { r.set("产品"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while (true) { r.get(); } } }
上面这个例子,因为在set、get方法中对flag的判断采用的是if,会导致生产者连续生产多个产品,后面的产品覆盖了前面的产品,消费者消费时只读到了最后一个产品。或者生产者生产了一个产品,而消费者反复消费了多次。
而当我们把if改为while后。执行完一次set或get后,在需要唤醒对方线程时,用notify,容易出现只唤醒本方线程的情况。会导致所有线程都进入wait()。
2、JDK1.5中提供了多线程升级解决方案。
Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。 Condition对象可以通过Lock锁进行获取,并支持多个相关的Condition对象。
/* 生产者生产商品,供消费者使用 有两个或者多个生产者,生产一次就等待消费一次 有两个或者多个消费者,等待生产者生产一次就消费掉 */ import java.util.concurrent.locks.*; public class ProducerConsumerDemo2{ public static void main(String[] args){ Product product = new Product(); Producer p = new Producer(product); Consumer c = new Consumer(product); Thread t1 = new Thread(p); Thread t2 = new Thread(p); Thread t3 = new Thread(c); Thread t4 = new Thread(c); t1.start(); t2.start(); t3.start(); t4.start(); } } class Product{ private String name; private int count=0; private boolean flag=false; private Lock lock = new ReentrantLock(); private Condition condition_pro = lock.newCondition(); private Condition condition_con = lock.newCondition(); public void set(String name)throws InterruptedException{ lock.lock(); try{ while(flag){ condition_pro.await(); } this.name=name; count++; System.out.println(Thread.currentThread().getName()+"Produce "+name+"-"+count); flag=true; condition_con.signal(); }finally{ lock.unlock(); } } public void get()throws InterruptedException{ lock.lock(); try{ while(!flag){ condition_con.await(); } System.out.println(Thread.currentThread().getName()+"consume ---------------- "+name+count); flag=false; condition_pro.signal(); }finally{ lock.unlock(); } } } class Producer implements Runnable{ private Product p; Producer(Product p){ this.p=p; } public void run(){ while(true){ try{ p.set("产品"); }catch(InterruptedException e){} } } } class Consumer implements Runnable{ private Product p; Consumer(Product p){ this.p=p; } public void run(){ while(true){ try{ p.get(); }catch(InterruptedException e){} } } }
八、停止线程
在JDK 1.5版本之前,有stop停止线程的方法,但升级之后,此方法已经过时。
那么现在我们该如果停止线程呢?
只有一种办法,那就是让run方法结束。
1、开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
如:run方法中有如下代码,设置一个flag标记。
public void run() { while(flag) { System.out.println(Thread.currentThread().getName()+"....run"); } }
那么只要在主函数或者其他线程中,在该线程执行一段时间后,将标记flag赋值false,该run方法就会结束,线程也就停止了。
2、上面的1方法可以解决一般情况,但是有一种特殊情况:就是当线程处于冻结状态。就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法interrupt();
如:
class StopThread2 implements Runnable { private boolean flag =true; public synchronized void run() { while(flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"....run"); } public void changeFlag() { flag = false; } } class StopThreadDemo2 { public static void main(String[] args) { StopThread2 st = new StopThread2(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.start(); t2.start(); int num = 0; while(true) { if(num++ == 60) { st.changeFlag();//改变循环标记 t1.interrupt();//清除冻结状态 t2.interrupt(); break; } System.out.println(Thread.currentThread().getName()+"......."+num); } System.out.println("over"); } }
九、什么时候写多线程?
当某些代码需要同时被执行时,就用单独的线程进行封装。
示例:
class ThreadDemo { public static void main(String[] args) { // 一条线程 new Thread() { public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().toString() + "....." + x); } } }.start(); // 又是一条线程 Runnable r = new Runnable() { public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + "....." + x); } } }; new Thread(r).start(); // 可以看作主线程 for (int x = 0; x < 1000; x++) { System.out.println("Hello World!"); } } }
扩展小知识:
1、join方法
当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。
/* join: 当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。 join可以用来临时加入线程执行。 */ class Demo implements Runnable { public void run() { for(int x=0; x<70; x++) { System.out.println(Thread.currentThread().toString()+"....."+x); Thread.yield();//暂停当前线程,让其他线程执行。 } } } class JoinDemo { public static void main(String[] args) throws Exception { Demo d = new Demo(); Thread t1 = new Thread(d); Thread t2 = new Thread(d); t1.start(); //t1.setPriority(Thread.MAX_PRIORITY); t2.start(); t1.join();//main主线程要等t1线程执行完之后才继续执行 for(int x=0; x<80; x++) { System.out.println("main....."+x); } System.out.println("over"); } }
2、setPriority()方法用来设置优先级
MAX_PRIORITY 最高优先级10
MIN_PRIORITY 最低优先级1
NORM_PRIORITY 分配给线程的默认优先级
3、yield()方法可以暂停当前线程,让其他线程执行。
发表评论
-
Reflect
2015-05-13 00:34 389第一讲 反射的应用 ... -
Regular Expression
2015-05-06 15:50 431正则表达式 一、概述 1、 概念:符合一定规则 ... -
网络编程
2015-05-04 12:07 443第一讲 概述 1、网络模型:OSI参考模型和TCP/ ... -
System、Runtime、Date、DateFormat、Calendar、Math、Random
2015-03-15 00:55 470第一讲 System类 一、概述 1、System是描 ... -
Generic 泛型
2015-03-13 23:23 523第一讲 泛型(Gene ... -
Java IO
2015-03-08 23:26 564第一讲 什么是IO? ...
相关推荐
一个进程中可以同时运行多个线程,每个线程可以执行不同的任务,这就是所谓的多线程。同一进程中的多个线程将共享该进程中的全部系统资源,如虚拟地址空间、文件描述符和信号处理等,但是同一个进程中的多个线程都有...
多线程是实现复杂任务并发执行的关键技术,能够提高资源利用率,优化系统响应时间。在STM32上实现多线程,通常会借助实时操作系统(RTOS)如RT-Thread。 RT-Thread是一个轻量级、开源的实时操作系统,它为STM32等微...
在计算机科学领域,多线程是一种程序设计技术,它允许应用程序同时执行多个任务或子任务。这极大地提高了软件的效率和响应性,特别是在现代多核处理器的环境下。本主题将深入探讨多线程的实现方式及其与单线程的对比...
"大漠多线程模板"是一个专门针对C#开发的多线程处理框架,它为开发者提供了便捷的方式来管理和优化多线程应用。这个框架由知名开发者"大漠"创建,旨在简化复杂的并发编程,提高代码的可读性和可维护性。 多线程允许...
安卓多线程,安卓高级开发中,必备技能,项目开发中都要使用
QTcpServer多线程 每个客户端连接的tcpSocket分别分配一个专门的线程来处理。 核心思想:继承并重写QTcpServer的incomingConnection函数去自己实现tcpsocket连接的建立和分配。 incomingConnection函数说明: 当...
多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多...
在IT行业中,多线程是程序设计中的一个重要概念,尤其在Java编程中,它被广泛应用于提高应用程序的并发性能和响应速度。本压缩包“多线程基础与基于多线程的简单聊天室”提供了对多线程技术的实践理解和二次开发的...
在IT领域,多线程是程序设计中的一个重要概念,尤其在现代计算机系统中,它能够提升应用程序的效率和响应性。MFC(Microsoft Foundation Classes)是微软提供的一个C++类库,用于简化Windows应用程序的开发,包括...
qt 使用QWebSocket 创建websocket客户端来读取数据,异步链接,并且放入到线程中去执行,线程池的基础,代码使用两个用户,放入到一个线程中执行,同理,可以多个用户放入到多个线程中执行,为线程池执行websocket ...
Java多线程是并发编程中的一个重要概念,它允许程序在同一时刻执行多个任务。以下是对Java多线程的深入理解: 线程概述 基本概念:线程是操作系统能够进行运算调度的最小单位,一个进程可以包含多个线程。 特性:...
在Windows Presentation Foundation(WPF)开发中,多线程是一个重要的技术,特别是在处理大量数据或进行耗时操作时,为了保持用户界面(UI)的响应性,通常会使用多线程来实现非UI任务。本实例是关于如何在WPF应用...
在Java编程中,多线程导入Excel数据是一项常见的任务,特别是在大数据处理和高并发场景下。这个场景通常涉及到性能优化和资源管理,以确保系统稳定性和数据一致性。下面将详细阐述多线程导入Excel数据的核心知识点。...
使用一个简单的数字递减案例,来模拟多线程下的工作逻辑
易语言关闭多线程句柄方法 易语言是一种功能强大且灵活的编程语言,多线程编程是其重要特性之一。然而,在多线程编程中,如何正确地关闭线程句柄是非常重要的。今天,我们将分享易语言关闭多线程句柄方法的相关知识...
Java多线程详解 在Java编程中,多线程是一种重要的技术,它使得程序能够同时执行多个任务,提高系统的效率和响应性。本教程将详细讲解Java中的多线程概念,包括线程的创建、状态、同步以及高级主题,旨在帮助初学者...
Java多线程是Java编程语言中一个非常重要的概念,它允许开发者在一个程序中创建多个执行线程并行运行,以提高程序的执行效率和响应速度。在Java中,线程的生命周期包含五个基本状态,分别是新建状态(New)、就绪...
易语言多线程启动详解 易语言多线程启动是指在易语言中使用多线程相关的API、支持库或模块来实现多线程编程的技术。多线程编程可以极大地提高程序的执行效率和响应速度,特别是在需要执行大量计算或I/O操作的场景下...
该文档是笔者在学习李刚老师《Java疯狂讲义》中有关多线程的用法而总结出来的笔记,其中主要的内容包括线程创建和启动、线程的生命周期、控制线程、线程同步、线程通信线程池等基本内容。对Java多线程有详细的介绍。
在C#编程中,多线程是一个至关重要的概念,尤其对于开发高性能、高并发的应用程序而言。本资源“C#多线程开发之并发编程经典实例”提供了丰富的实例,旨在帮助C#开发者深入理解并掌握多线程技术。以下是关于C#多线程...