Android学习笔记——java线程基础
来源:互联网 发布:电源网络二合一防雷器 编辑:程序博客网 时间:2024/05/21 15:41
java线程
1、java线程的理解
线程就是程序执行代码块的过程:它会与各种状态,或者叫生命周期:创建、就绪(启动)、执行、阻塞、睡眠、结束(消亡),
上面这个顺序是不确定的
注意区分系统的进程与java的线程的区别,以及cpu时间片对java线程的影响
1.1 java虚拟机JVM是进程,在windows任务管理器中显示的java.exe
1.2 一个普通java程序启动后,main方法所在的线程是主线程,这时java后台还有一个线程:负责垃圾回收,释放内存空间
1.3 多线程:即多个线程同时运行。
特点:并发执行,提高效率,消耗资源较大
1.4 java创建线程是通过调用底层系统的接口来实现的
1.5 在java中创建线程有两种方式:一种是继承Thread类,另外一种是实现Runable接口
1.6 通过继承Thread方法创建线程:
*继承Thread类
*创建此对象,并调用start()方法
- package thread;
- public class TestThreadCreat{
- public static void main(String[] args) {
- ThreadCreat t1 = new ThreadCreat("threadCreat1");
- ThreadCreat t2 = new ThreadCreat("threadCreat2");
- t1.start();
- t2.start();
- }
- }
- class ThreadCreat extends Thread{
- public ThreadCreat(String name){
- super(name);
- }
- public void run(){
- int a = 1;
- while(a<=100){
- System.out.println(this.getName()+"__"+a++);
- }
- }
- }
1.7 通过实现Runable接口创建线程:
步骤:
*定义类实现Runnable接口
*覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。
*通过Thread类建立线程对象。
*将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
*调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
- package thread;
- public class TestRunnableCreate {
- public static void main(String[] args) {
- RunnableCreate runnableCreate1 = new RunnableCreate();
- RunnableCreate runnableCreate2 = new RunnableCreate();
- new Thread(runnableCreate1).start();
- new Thread(runnableCreate1).start();
- }
- }
- class RunnableCreate implements Runnable {
- public void run(){
- int a = 1;
- while(a<=100){
- System.out.println(Thread.currentThread().getName()+"__"+a++);
- }
- }
- }
2 多线程的安全问题:当多个线程同时对相同的资源进行操作时,就有可能导致访问的资源不准确,这种情况就称之为线程安全问题。
- package thread;
- /*
- * 不安全的多线程售票,可能出现负票
- */
- public class TestThreadDanger {
- public static void main(String[] args) {
- Ticket t1 = new Ticket();
- new Thread(t1,"TicketSaleMan1").start();
- new Thread(t1,"TicketSaleMan2").start();
- new Thread(t1,"TicketSaleMan3").start();
- new Thread(t1,"TicketSaleMan4").start();
- }
- }
- class Ticket implements Runnable {
- private int tick = 100;
- public void run(){
- while(true){
- if(tick>0){
- try{Thread.sleep(20);}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+"__sale:"+tick--);
- }
- else{
- break;
- }
- }
- }
- }
2.1 java中通过同步这个概念来解决线程安全问题
即,将多个线程共通操作的资源进行访问控制(锁/锁旗标),在某一个时刻只能一天线程进行操作。关键字是:syschronized
同步的前提:*两个及两个以上线程
*多个线程的访问控制使用同一个锁
2.2 java中同步有 种实现方式:同步代码块,同步函数(静态同步函数和非静态同步函数)
2.2.1 同步代码块:
将多线程共通执行的代码块放到同步代码块中执行,这样就可以通过同步代码块的锁进行访问控制。
如果一个线程先持有同步代码块的锁,则在它释放这个锁之前,其它现在只能在外面等待
- package thread;
- /*
- *增加同步代码块后,这个程序是线程安全的
- */
- public class TestThreadDanger {
- public static void main(String[] args) {
- Ticket t1 = new Ticket();
- new Thread(t1, "TicketSaleMan1").start();
- new Thread(t1, "TicketSaleMan2").start();
- new Thread(t1, "TicketSaleMan3").start();
- new Thread(t1, "TicketSaleMan4").start();
- }
- }
- class Ticket implements Runnable {
- private int tick = 100;
- Object obj = new Object();
- public void run() {
- while (true) {
- //同步代码块,将同步代码放入同步代码块中执行
- synchronized (obj) {// obj为自己创建的锁旗标
- if (tick > 0) {
- try {
- Thread.sleep(20);
- }
- catch (Exception e) {
- }
- System.out.println(Thread.currentThread().getName() + "__sale:" + tick--);
- }
- else {
- break;
- }
- }
- }
- }
- }
2.2.2 同步函数:
将多线程并发访问的代码封装到函数里面,然后在函数声明的时候加上synchronized,进行同步访问控制。
同步函数的锁是this,即本对象
- package thread;
- /*
- * 同步函数
- */
- public class TestThreadDanger {
- public static void main(String[] args) {
- Ticket t1 = new Ticket();
- new Thread(t1, "TicketSaleMan1").start();
- new Thread(t1, "TicketSaleMan2").start();
- new Thread(t1, "TicketSaleMan3").start();
- new Thread(t1, "TicketSaleMan4").start();
- }
- }
- class Ticket implements Runnable {
- private int tick = 100;
- Object obj = new Object();
- boolean haveTicket = true;
- public void run() {
- while (true) {
- if (haveTicket)
- saleTicket();
- else
- break;
- }
- }
- public synchronized void saleTicket(){
- if (tick > 0) {
- try {Thread.sleep(20);}catch (Exception e) {}
- System.out.println(Thread.currentThread().getName() + "__sale:" + tick--);
- }else{
- haveTicket = false;
- }
- }
- }
2.2.3 静态同步函数
静态同步函数的锁是该类的字节码文件对象
- class Ticket implements Runnable
- {
- private static int tick = 100;
- //Object obj = new Object();
- boolean flag = true;
- public void run()
- {
- if(flag)
- {
- while(true)
- {
- synchronized(Ticket.class)
- {
- if(tick>0)
- {
- try{Thread.sleep(10);}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
- }
- }
- }
- }
- else
- while(true)
- show();
- }
- public static synchronized void show()
- {
- if(tick>0)
- {
- try{Thread.sleep(10);}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
- }
- }
- }
- class StaticMethodDemo
- {
- public static void main(String[] args)
- {
- Ticket t = new Ticket();
- Thread t1 = new Thread(t);
- Thread t2 = new Thread(t);
- t1.start();
- try{Thread.sleep(10);}catch(Exception e){}
- t.flag = false;
- t2.start();
- }
- }
2.3 使用静态同步函数实现单例设计模式的懒汉式
- /*
- * 懒汉式,或者延迟加载,当需要的时候才创建这个类
- * 懒汉式要注意的是线程安全问题
- */
- class SingletonDelay{
- private SingletonDelay(){};
- private static SingletonDelay sin = null;
- public static SingletonDelay getInstence(){
- if(sin == null){//双重判断的目的是为了减少锁的比较次数,提高效率
- synchronized(SingletonDelay.class){//静态方法的锁必须用类的字节码对象
- if(sin==null){
- sin = new SingletonDelay();
- }
- }
- }
- return sin;
- }
- }
2.4 死锁
死锁,当多个线程之间并发执行,出现线程无限等待的现象称为死锁。
死锁产生的可能原因:
--A线程持有锁1,A线程现在需要锁2里面被控制的资源;B线程持有锁2,B线程现在需要锁1里面被控制的资源。因此线程A与B陷入相互需要对方资源,恶性循环中。
--嵌套锁:锁里面有锁
- /*
- 死锁。
- 同步中嵌套同步。
- */
- class Ticket implements Runnable
- {
- private int tick = 1000;
- Object obj = new Object();
- boolean flag = true;
- public void run()
- {
- if(flag)
- {
- while(true)
- {
- synchronized(obj)
- {
- show();
- }
- }
- }
- else
- while(true)
- show();
- }
- public synchronized void show()//this
- {
- synchronized(obj)
- {
- if(tick>0)
- {
- try{Thread.sleep(10);}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
- }
- }
- }
- }
- class DeadLockDemo
- {
- public static void main(String[] args)
- {
- Ticket t = new Ticket();
- Thread t1 = new Thread(t);
- Thread t2 = new Thread(t);
- t1.start();
- try{Thread.sleep(10);}catch(Exception e){}
- t.flag = false;
- t2.start();
- }
- }
2.5 多线程通信
多线程通信:等待/唤醒机制.即多个线程并发访问相同资源时,分工明确的线程,一个负责生产,一个负责消费,那么在这种情况下,
资源对于它们来时就是一个需要同步访问控制的东西。资源没有了,消费者就要通知生产者生产,消费者自己等待。生产者生产好
资源了,就应该通知消费者消费,自己等待。这就是多线程通信。
2.5.1 等待唤醒机制
在jdk1.5之前等待唤醒机制是通过在同步块里面使用方法:wait() :使线程进入冻结状态,,notify():唤醒线程池中排序的第一个线程,
notifyAll()唤醒线程池中所有等待的线程。
线程通信示例:
- class ProducerConsumerDemo
- {
- public static void main(String[] args)
- {
- Resource r = new Resource();
- Producer pro = new Producer(r);
- Consumer con = new Consumer(r);
- Thread t1 = new Thread(pro);
- Thread t2 = new Thread(pro);
- Thread t3 = new Thread(con);
- Thread t4 = new Thread(con);
- t1.start();
- t2.start();
- t3.start();
- t4.start();
- }
- }
- /*
- 对于多个生产者和消费者。
- 为什么要定义while判断标记。
- 原因:让被唤醒的线程再一次判断标记。
- 为什么定义notifyAll,
- 因为需要唤醒对方线程。
- 因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。
- */
- class Resource
- {
- private String name;
- private int count = 1;
- private boolean flag = false;
- // t1 t2
- public synchronized void set(String name)
- {
- while(flag)
- try{this.wait();}catch(Exception e){}//t1(放弃资格) t2(获取资格)
- this.name = name+"--"+count++;
- System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
- flag = true;
- this.notifyAll();
- }
- // t3 t4
- public synchronized void out()
- {
- while(!flag)
- try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格)
- System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
- flag = false;
- this.notifyAll();
- }
- }
- class Producer implements Runnable
- {
- private Resource res;
- Producer(Resource res)
- {
- this.res = res;
- }
- public void run()
- {
- while(true)
- {
- res.set("+商品+");
- }
- }
- }
- class Consumer implements Runnable
- {
- private Resource res;
- Consumer(Resource res)
- {
- this.res = res;
- }
- public void run()
- {
- while(true)
- {
- res.out();
- }
- }
- }
2.5.2 JDK1.5对线程锁的升级
在JDK 1.5中,提供了改进synchronized的升级解决方案。将同步synchronized替换为显式的Lock操作,将Object中的wait,notify,notifyAll替换成Condition对象,该对象可对Lock锁进行获取。wait,notify,notifyAll这些方法都定义在Object中,是因为这些方法操作同步中的线程时,都必须表示自己所操作的线程的锁,就是说,等待和唤醒的必须是同一把锁。不可对不同锁中的线程进行唤醒。所以这就使得程序是不良的,因此,通过对锁机制的改良,使得程序得到优化。
- /*
- 线程间通信:
- 等待唤醒机制:升级版
- 生产者消费者 多个
- */
- import java.util.concurrent.locks.*;
- class ProducerConsumerDemo{
- public static void main(String[] args){
- Resouse r = new Resouse();
- Producer p = new Producer(r);
- Consumer c = new Consumer(r);
- Thread t1 = new Thread(p);
- Thread t2 = new Thread(c);
- Thread t3 = new Thread(p);
- Thread t4 = new Thread(c);
- t1.start();
- t2.start();
- t3.start();
- t4.start();
- }
- }
- class Resouse{
- private String name;
- private int count = 1;
- private boolean flag = false;
- private Lock lock = new ReentrantLock();
- private Condition condition_P = lock.newCondition();
- private Condition condition_C = lock.newCondition();
- //要唤醒全部,否则都可能处于冻结状态,那么程序就会停止。这和死锁有区别的。
- public void set(String name)throws InterruptedException{
- lock.lock();
- try{
- while(flag)//循环判断,防止都冻结状态
- condition_P.await();
- this.name = name + "--" + count++;
- System.out.println(Thread.currentThread().getName() + "..生成者--" + this.name);
- flag = true;
- condition_C.signal();
- }finally{
- lock.unlock();//释放锁的机制一定要执行
- }
- }
- public void out()throws InterruptedException{
- lock.lock();
- try{
- while(!flag)//循环判断,防止都冻结状态
- condition_C.await();
- System.out.println(Thread.currentThread().getName() + "..消费者." + this.name);
- flag = false;
- condition_P.signal();//唤醒全部
- }finally{
- lock.unlock();
- }
- }
- }
- class Producer implements Runnable{
- private Resouse r;
- Producer(Resouse r){
- this.r = r;
- }
- public void run(){
- while(true){
- try{
- r.set("--商品--");
- }catch (InterruptedException e){}
- }
- }
- }
- class Consumer implements Runnable{
- private Resouse r;
- Consumer(Resouse r){
- this.r = r;
- }
- public void run(){
- while(true){
- try{
- r.out();
- }catch (InterruptedException e){}
- }
- }
- }
2.6 停止线程
从JDK1.5自后线程里面的方法stop()显示已经过时,那么我们怎么停止线程呢?肯定是结束run()方法呀,怎么结束run()方法呢?一般情况下,run方法里面就有while循环,我们结束循环就可以了。
通过标记结束循环:
- 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() + "----Exception");
- flag = false;
- }
- System.out.println(Thread.currentThread().getName() + "----run");
- }
- }
- public void changeFlag(){
- flag = false;
- }
- }
- class StopThreadDemo{
- public static void main(String[] args) {
- StopThread st = new StopThread();
- Thread t1 = new Thread(st);
- Thread t2 = new Thread(st);
- t1.start();
- t2.start();
- int n = 0;
- while (true){
- if (n++ == 60){
- st.changeFlag();
- break;
- }
- System.out.println("Hello World!");
- }
- }
- }
上面有特殊情况:当线程处于冻结状态,就不会读取标记,那么线程就不会结束
因此我们必须使用Thread类里面提供的interrupt()方法,来中断线程,但不会让它死掉,会让线程脱了冻结状态,恢复到运行状态,再去判断标记,然后结束循环,最终跳出run方法。
- 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() + "----Exception");
- flag = false;
- }
- System.out.println(Thread.currentThread().getName() + "----run");
- }
- }
- }
- class StopThreadDemo{
- public static void main(String[] args){
- StopThread st = new StopThread();
- Thread t1 = new Thread(st);
- Thread t2 = new Thread(st);
- t1.start();
- t2.start();
- int n = 0;
- while (true){
- if (n++ == 60){
- t1.interrupt();
- t2.interrupt();
- break;
- }
- System.out.println("Hello World!");
- }
- }
- }
2.7 守护线程
在线程创建后,在启动以前,调用方法setDeamon(),可以将一个线程标记为守护线程。如果这个线程结束,则java虚拟机也会结束。
2.8 join线程
当A线程执行到B线程方法时,A线程就会等待,B线程都执行完,A才会执行。join可用来临时加入线程执行。
- class Demo implements Runnable{
- public void run(){
- for(int x=0;x<90;x++){
- System.out.println(Thread.currentThread().getName() + "----run" + x);
- }
- }
- }
- 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();
- t2.start();
- t1.join();//等t1执行完了,主线程才从冻结状态恢复,和t2抢执行权。t2执不执行完都无所谓。
- int n = 0;
- for(int x=0;x<80;x++){
- System.out.println(Thread.currentThread().getName() + "----main" + x);
- }
- System.out.println("Over");
- }
- }
2.9 其它方法:
设置线程优先级:可通过setPriority()设定,如:setPriority(Thread.MAX_PRIORITY)设优先级为最大。
不执行这个线程,跳到其它线程:yield() 通过这个方法,可稍微减少线程执行频率,达到线程都有机会平均被执行的效果。
- Android学习笔记——java线程基础
- 黑马程序员—Java基础学习笔记之多线程
- java基础学习—线程
- java线程基础——笔记
- java线程基础——笔记2
- 【Java基础】线程笔记——ThreadApi
- 【Java基础】线程笔记——synchronized
- 【java基础】线程笔记——LockSupport
- 【Java基础】线程笔记——线程安全
- Java基础学习总结——线程
- java基础学习总结—— 线程
- 黑马程序员——java语言基础——线程学习笔记二
- 黑马程序员—11—java基础:有关线程通信的学习笔记和学习心得体会
- Java 学习笔记——线程
- java线程——学习笔记
- Java多线程学习笔记—线程停止
- 笔记-java线程基础
- Java基础—线程
- 共享库的使用
- 我常用的网络测试工具
- UserCF和ItemCF的联系和区别
- mysql,oracle的关键字(保留字)整理
- IOS , plist 配置项说明
- Android学习笔记——java线程基础
- 记一次 superblock 损坏导致服务器无法启动的故障修复
- Avro总结(RPC/序列化)
- Java Web层的下一个王者是谁?
- codeforces 161D - Distance in Tree 树状DP
- 关于论坛数据库的设计(分表分库等-转)
- PHP 系统变量 $_SERVER
- GCD
- MATLAB中全局变量的认识——GUI