多线程学习笔记(一)
来源:互联网 发布:快3遗漏号码数据查询 编辑:程序博客网 时间:2024/09/21 06:36
1、在java中要实现多线程,有两种手段,一种是继承Thread类,另外一种是实现Runable接口
1)继承了Thread类
public class HelloThread extends Thread{ private String name; public HelloThread(String name) { this.name = name; } @Override public void run() { for(int i = 0; i < 5; i++) System.out.println( name + "运行第 " + i + " 次"); } public static void main(String[] arags) { HelloThread h1 = new HelloThread("线程A"); HelloThread h2 = new HelloThread("线程B"); h1.start(); h2.start(); }}
2)实现了Runnable接口
public class HelloRunnable implements Runnable{ private String name; public HelloRunnable(String name) { this.name = name; } @Override public void run() { for(int i = 0; i < 5; i++) System.out.println( name + "运行第 " + i + " 次"); } public static void main(String[] args) { HelloRunnable h1 = new HelloRunnable("线程A"); Thread t1 = new Thread(h1); HelloRunnable h2 = new HelloRunnable("线程B"); Thread t2 = new Thread(h2); t1.start(); t2.start(); }}
Thread其实也是实现了Runnable接口
结果:
①该对象没有被当前线程锁住,则调用wait方法报错:
结果:
结果:
结果:
class Thread implements Runnable { //…public void run() { if (target != null) { target.run(); } }}
Thread和Runnable的区别:
如果一个类继承Thread,则不适合资源共享,但用Runnable接口的类就可以实现资源共享。同时避免了单继承,所以推荐使用Runnable作为多线程的开启方法
public class MyThread implements Runnable{ //多个线程处理一个对象可实现资源共享 private int ticket = 5; @Override public void run() { for(int i = 0; i <= 20; i++) { if(ticket > 0) { System.out.println(Thread.currentThread().getName() + "正在售票" +ticket--); } } } public static void main(String[] args) { MyThread my = new MyThread(); new Thread(my, "一号窗口").start(); new Thread(my, "二号窗口").start(); new Thread(my, "三号窗口").start(); }}
2、线程使用的方法有:
1)isAlive 判断线程是否还在运行
public static void main(String[] args) { Thread t1 = new Thread(); System.out.println("线程启动前: " + t1.isAlive()); t1.start(); System.out.println("线程启动后: " + t1.isAlive()); }
2)join 在当前线程中调用另外一个线程的join方法,则会阻塞当前运行的线程直到调用join方法的线程执行完毕再继续执行,换句话说,如果调用join方法的线程没有执行完,则当前线程会一直等待,但其它无关线程不受阻碍。
一般是用于使用子线程执行耗时操作,当要使用子线程中的结果时调用该方法把结果执行完毕再往下执行。
public static void main(String[] argss) { Thread t1 = new Thread(new Runnable() { @Override public void run() { try { for(int i = 0; i < 3; i++) { System.out.println("子线程: " +Thread.currentThread().getName()); Thread.sleep(1000); //sleep 1 秒 } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); //开启线程 t1.start(); for(int i = 0; i < 50; i++) { if(i > 8) { try { t1.join(); //当大于8时把t1线程执行完再往下执行 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("主线程执行到第 " + i + "次"); } }
结果:
可以看到当线程达到第8次时会先把子线程执行完在往下执行,你自己可以尝试一下。
3)wait 释放锁对象并等待,如果等待对象没有被锁住,则抛出异常IllegalMonitorStateException,换句话说,需要使用对象的wait方法,则需要在synchronized 语句块或方法体内使用。wait方法不是线程的方法,而是每一个对象的方法。所以针对的是访问该对象的当前线程的阻塞。
①该对象没有被当前线程锁住,则调用wait方法报错:
public static void main(String[] argss) { Object object =new Object(); try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }
②使用synchronized语句块
public static void main(String[] argss) { Object object =new Object(); synchronized (object) { try { object.wait(); //阻塞访问当前对象的线程,即主线程,所以下面这句话永远不会打印出来 System.out.println("当前线程为: " + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }
4)notify 唤醒在等待该对象锁的线程,每次只唤醒一个线程,同时不能控制唤醒的是哪一个线程。一般与wait互用
我们还是使用上一个例子,但是因为当前线程已被阻塞,我想你不会在当前线程中使用objecty.notify来调用吧。我们需要另外一个线程中唤醒被阻塞的线程。
//将对象变成静态类变量实现两个线程之间共享 static Object object =new Object(); public static void main(String[] argss) { //使用t1对主线程进行唤醒操作 Thread t1 = new Thread(new Runnable() { @Override public void run() { while(true) { //因为不知道几时进行阻塞状态,所以需要不断循环 System.out.println("子线程运行中..."); synchronized (object) { object.notify(); //唤醒主线程操作 System.out.println("唤醒后我会不会继续操作。。"); //调用notify之后不会阻塞当前线程,其它线程会等待当前线程执行完再争夺对象锁 } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }); t1.start(); //开启子线程 synchronized (object) { try { System.out.println("主线程进行wait等待中"); object.wait(); //阻塞访问当前对象的线程,即主线程 System.out.println("当前线程为: " + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }
结果:
如果操作的对象不是同一个,wait和notify也就没什么意义了。
5)synchronized关键字 用于保证同一时刻最多只有一个线程执行该代码段,用于锁定方法或者代码块
①synchronized方法
如:public synchronized void accessVal(int newVal); 即创建该方法的实例并调用改方法时,同时刻只会有一个线程进行操作,锁对象是该方法的实例。如果是静态类方法,则获得的是类锁,类锁不是把所有该类对象实例都加锁,而是单单只是类信息,即Object.class对象。 如: public synchronized static void accessVal(int newVal);
②synchronized快
上面代码中也有用到,即synchronized(要加锁的对象),可以使用synchronized(this)锁住当前对象实例,或者锁住你需要的共享对象。用法比较简单。
6)interrupt 通过抛出一个中断信号,让线程获得InterruptException,从而退出阻塞状态,但不会中断一个正在运行的线程 。如用Object.wait Thread.join 和 Thread.sleep阻塞,都可以使用interrupt方法提前退出阻塞。(个人觉得比较少用)
①.sleep() & interrupt()
public static void main(String[] args) { //这里有两个线程threadA和threadB,将threadA调用sleep方法,并在threadB中调用threadA.interrupt()方法触发异常。 final Thread threadA = new Thread(new Runnable() { public void run() { try { System.out.println("进入sleep状态"); Thread.sleep(100000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("捕获Exception后运行的一行。。"); } }); Thread threadB = new Thread(new Runnable() { public void run() { threadA.interrupt(); } }); threadA.start(); //避免那么快执行完,先sleep一下 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } threadB.start(); }
结果:
②join() & interrupt()
interrupt的调用者一定是当前被阻塞的线程,同时这个被阻塞的线程由其他线程调用。这样才能发挥功效
public static void main(String[] args) { //这里有三个线程。 //线程A会在i = 8时调用线程C的join方法,即自己被阻塞,让线程C方法运行完再继续运行 //线程B会调用线程A的interrupt方法提前退出join方法 final Thread threadC = new Thread(new Runnable() { public void run() { for(int i = 0; i < 10; i++) { System.out.println("ThreadC 线程执行第: " + i + "次" ); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }); final Thread threadA = new Thread(new Runnable() { public void run() { for(int i = 0; i < 30; i++) { if(i == 8) { try { threadC.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("ThreadA 线程执行第: " + i + "次" ); } } }); Thread threadB = new Thread(new Runnable() { public void run() { while(true) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } threadA.interrupt(); } } }); threadA.start(); threadB.start(); threadC.start(); }
结果:
③wait() & interrupt()
public static void main(String[] argss) { final Object object = new Object(); final Thread threadA = new Thread(new Runnable() { @Override public void run() { System.out.println("线程A开始执行..."); synchronized (object) { try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程A继续执行。。。"); } } }); threadA.start(); //开启子线程 Thread threadB = new Thread(new Runnable() { @Override public void run() { System.out.println("线程B开始执行"); //如果太快是会先打印出线程A继续执行。。而不是线程B开始执行 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } threadA.interrupt(); } }); threadB.start(); }}
7) Thread.yield() 暂停线程的执行,给其它具有相同优先权的线程执行的集合,若没有其它线程执行,则线程继续执行。创建一个线程默认优先级为5,可以使用setPriority进行设置优先级。
补充:线程有4个状态
新建状态New 线程处于新建状态,还没调用start方法
就绪状态Runnable:线程对象创建后,其它线程调用了该对象的start方法,该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
运行状态Running:就绪状态的线程获得了CPU,执行程序代码。
阻塞状态 Blocked:阻塞状态时线程因为某种原因放弃CPU使用权,暂时停止运行。需要重新进入就绪状态,等待获得CPU使用时间片
(一)等待阻塞:运行的线程执行wait方法,JVM会把线程放入等待池
(二)同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入池中
(三)其它阻塞:运行的线程执行sleep或join方法,或者发出了I/0请求时,JVM会把线程设为阻塞状态,当之前方法完毕之后,线程重新转入就绪状态。
死亡状态Dead:线程执行完或者异常退出,则该线程结束生命周期
8)suspend和resume。因为suspend容易发生死锁,调用suspend方法时,目标线程会停下来,但仍会拥有之前获得的锁。所以不建议使用。
0 0
- 多线程学习笔记(一)
- 多线程学习笔记(一)
- 多线程学习笔记(一)
- 多线程学习笔记(一)
- 多线程学习笔记 一
- 多线程学习笔记一
- 多线程学习笔记一
- win32多线程学习笔记(一)
- 多线程编程学习笔记(一)
- jdk5.0多线程学习笔记(一)
- VC多线程编程学习笔记(一)
- 多线程多任务学习笔记(一)
- Posix多线程编程学习笔记(一)
- C#多线程学习笔记(一)
- Posix多线程编程学习笔记(一)
- Java多线程 学习笔记(一)
- Java多线程学习笔记(一)
- JAVA学习笔记--多线程(一)
- linux监控工具vmstat使用
- IntelliJ IDEA 偏好设置
- touch事件处理流程图
- iOS的主要框架介绍
- STL入门第四篇——唯美主义的杰作
- 多线程学习笔记(一)
- iOS 选择tableViewCell中的控件确定cell位置
- 从几个指标谈windows内存
- fastjson 序列化 忽略 某个字段
- 汇编指令优化惹的祸之 JS 跳转指令
- mongodb 查询操作,条件查询,where,find等常用操作
- dedecms下仿chinaz二级下拉动态读取代码
- JavaScript:Object.prototype.toString方法的原理
- Modem Crash 问题处理及注意事项