java多线程:线程安全问题,synchronized关键字,同步锁,线程停止方式。
2018-09-18 18:32
555 查看
线程安全问题,synchronized,同步锁
package com.qianfeng.test; /* 线程安全问题: * 分析:4个线程共用了一个数据,出现了-1,-2,-3等错误的数据 * * 具体分析:1.共用了一个数据 * 2.共享语句有多条,一个线程使用cpu,没有使用完,cpu被抢走,当再次抢到cpu的时候,直接执行后面的语句,造成了错误的发生. * * 解决: * 在代码中使用同步代码块儿(同步锁) * 解释:在某一段任务中,同一时间只允许一个线程执行任务,其他的线程即使抢到了cpu,也无法进入当前的任务区间,只有当当前的线程将任务执行完后, * 其他的线程才能有资格进入 * * 同步代码块儿的构成: * synchronized(锁(对象)){ * 同步的代码 * } * * 对作为锁的对象的要求:1.必须是对象 2.必须保证被多个线程共享 * 可以充当锁的:1.一个普通的对象 2.当前对象的引用--this 3.类的字节码文件 * * 同步代码块儿的特点:1.可以保证线程的安全 2.由于每次都要进行判断处理,所以降低了执行效率 * * 总结:什么时候使用同步代码块儿 * 1.多个线程共享一个数据 * 2.至少有两个线程 */ //第二种:线程与任务分离 public class Demo3 { public static void main(String[] args) { //创建任务对象 Ticket1 ticket = new Ticket1(); //创建线程对象并关联同一个任务 //如果我们创建了自己独立的任务类,线程会优先调用我们手动传入线程的任务类对象的run方法,不会再去调用Thread默认的run方法 Thread seller1 = new Thread(ticket); Thread seller2 = new Thread(ticket); Thread seller3 = new Thread(ticket); Thread seller4 = new Thread(ticket); //开启线程 seller1.start(); seller2.start(); seller3.start(); seller4.start(); } } //创建任务类 class Ticket1 implements Runnable{ //因为Ticket对象被四个线程共享,所以num作为属性也被共享了 int num = 20; boolean flag = false; //让object充当锁 //作为锁要满足两个条件:1.必须是对象 2.必须供所有的线程共享. //可以作为锁的有:1.任意一个实例对象 2.this 3.字节码文件对象 Object object = new Object(); public void run() { while (!flag) { synchronized (object) {//同步代码块儿--让线程之间互斥 //制造一个延迟,相当于让当前执行run的线程休息一会儿(临时让出cpu) try { Thread.sleep(100);//100是时间,单位是毫秒 } catch (InterruptedException e) { e.printStackTrace(); } if (num >0) { System.out.println(Thread.currentThread().getName()+" "+ --num); }else { flag = true; } } } } }
synchronized关键字的理解
package com.qianfeng.test; /* * 实例:两个人向同一个账户里面存钱 * 一人存三次,每次存100 * * * 注意:1.当在一个类中同时存在多个synchronized修饰的代码块儿或函数时,要想安全,就必须让他们后面的对象一致。因为只有同一把锁才能安全。 * 同步函数的锁:this * 2静态同步函数在进内存的时候不会创建对象,但是存在其所属类的字节码文件对象,属于class类型的对象,所以 * 静态同步函数的锁是其所属类的字节码文件对象 * * * * 理解synchronized关键字 * 1、synchronized关键字的作用域有二种: 1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象 的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的 一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。 这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时 访问相同类的另一个对象实例中的synchronized方法; 2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问 这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。 2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中, 表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/区块/},它的作用域是当前对象; 3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定 它的某个方法为synchronized方法; */ public class Demo4 { public static void main(String[] args) { //1.创建任务类对象 CunQian cunQian = new CunQian(); //2.创建线程并绑定任务 Thread thread1 = new Thread(cunQian); Thread thread2 = new Thread(cunQian); //3.开启线程 thread1.start(); thread2.start(); } } class Bank{ int sum;//放的是当前账户的钱 //使用同步代码块儿 // public void addMoney(int money){ // synchronized (this) { // sum+=money; // System.out.println(sum); // } // } //使用同步函数 //非静态的同步函数 //相当于默认在synchronized后面跟着this充当锁 // public synchronized void addMoney(int money){ // sum+=money; // System.out.println(sum); // } //静态的同步函数 //相当于默认在synchronized后面跟着当前的类的字节码文件充当锁----Bank.class public synchronized static void addMoney(int money){ } } //创建任务类 class CunQian implements Runnable{ Bank bank = new Bank(); public void run() { for(int i=0;i<3;i++){ bank.addMoney(100); } } }
懒汉式单例中使用同步代码块
package com.qianfeng.test; /* * 懒汉式单例中使用同步代码块 */ public class Demo5 { } //懒汉式 class SingleInstance{ private static SingleInstance singleInstance = null; public SingleInstance() { } //因为同步代码块的效率高于同步函数,所以尽量使用同步代码块 public static SingleInstance getInstance(){ if(singleInstance==null){//目的:尽量减少线程安全代码的判断次数,提高效率。 synchronized(SingleInstance.class){ if(singleInstance==null){ singleInstance = new SingleInstance(); } } } return singleInstance; } } //饿汉式 class SingleInstance1{ private final static SingleInstance1 singleInstance = new SingleInstance1(); public SingleInstance1() { } public static SingleInstance1 getInstance(){ return singleInstance; } } class Test implements Runnable{ @Override public void run() { SingleInstance1 singleInstance1 = SingleInstance1.getInstance(); } }
package com.qianfeng.test; public class Demo6 { public static void main(String[] args) { Thread thread1 = new Thread(); //1.可以,这里将thread1当作了任务类对象,执行的时候调用的是thread1内部的run方法 Thread thread2 = new Thread(thread1); thread2.start(); //2.创建Thread类的匿名子类对象充当线程类 new Thread(){ public void run(){ System.out.println("hah"); }; }.start(); } }
线程停止的3种方式:标识,stop,interrupt()方法。wait()方法。
package com.qianfeng.test; /* * 线程的停止:如何让它的任务结束 * 1.通过一个标识去结束线程 * 2.通过调用stop方法去结束线程--有固有的安全问题,已经过时,不建议再使用。 * 3.调用interrupt()方法结束线程 * 原理:线程可以调用wait()方法,让当前的线程处于钝化的状态(会立刻释放cpu,并且处于无法抢cpu的状态,但是当前的线程并没有死亡) * 注意点:wait方法必须在同步状态下使用。 * 调用interrupt方法就是将wait状态的线程停止。 */ //1.通过标识去结束进程 //public class Demo7 { // // public static void main(String[] args) { // // Test1 test1 = new Test1(); // Thread thread = new Thread(test1); // thread.start(); // // //让主线程睡一会 // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // // int i = 0; // while(true){ // if (++i == 10) { // test1.flag = false;//当主线程执行到某个阶段的时候,让flag值变成false,控制while循环的结束,从而控制子线程的结束 // break;//目的:让主线程结束 // } // } // } //} // //class Test1 implements Runnable{ // // boolean flag = true; // public void run() { // while(flag){ // System.out.println(Thread.currentThread().getName()+" "+"我们很happy"); // } // } //} //3.通过调用interrupt()方法结束线程 public class Demo7 { public static void main(String[] args) { Test1 test1 = new Test1(); Thread thread = new Thread(test1); thread.start(); //让主线程睡一会 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } int i = 0; while(true){ if (++i == 10) { thread.interrupt();//当调用这个方法的时候,会触发wait方法的InterruptedException异常 //我们就可以在捕获异常的时候将flag的值变成false,从而结束循环、任务、线程。 break;//目的:让主线程结束 } } } } class Test1 implements Runnable{ boolean flag = true; public synchronized void run() { while(flag){ try { this.wait(); } catch (InterruptedException e) { flag = false; System.out.println("InterruptedException"); }//wait方法由锁对象来调用 System.out.println(Thread.currentThread().getName()+" "+"我们很happy"); } } }阅读更多
相关文章推荐
- java中同步synchronized的意义,如何用它解决线程不安全的问题
- JAVA基础再回首(二十四)——多线程的概述、实现方式、线程控制、生命周期、多线程程序练习、安全问题的解决
- 实例解析Java中的synchronized关键字与线程安全问题
- Java笔记3 多线程<1>线程概述、多线程的创建、多线程的安全问题、静态同步函数的锁、死锁
- Java 中的多线程-两种创建方式,定时器的应用,线程的安全问题可以用银行转账来说明
- 实例解析Java中的synchronized关键字与线程安全问题
- 【Java多线程】多线程的线程安全及同步(synchronized)用法
- 记录Java多线程的同步问题---synchronized关键字的使用
- 并发编程学习笔记二 ------synchronized关键字实现同步访问解决多线程安全问题
- Java如何利用synchronized处理多线程的数据同步问题
- java高级多线程编程--关于线程的停止问题
- java多线程学习一线程安全之内存、synchronized、volatile
- java基础问题---java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用
- Java线程同步机制synchronized关键字的理解
- 初学Java多线程:使用Synchronized关键字同步类方法
- java高级多线程编程--关于线程的停止问题
- 九、初学Java多线程:使用Synchronized关键字同步类方法
- java高级多线程编程--关于线程的停止问题
- java高级多线程编程--关于线程的停止问题
- JAVA线程安全之synchronized关键字的正确用法