java中的synchronized

来源:互联网 发布:java文件上传下载原理 编辑:程序博客网 时间:2024/05/21 15:42

一.线程进程

首先理解什么是线程和进程?
进程:从操作系统核心角度来讲,进程是操作系统调度和分配内存资源,CPU时间片的基本单位,为应用程序提供运行环境,简单一点,进程是应用程序的一次运行活动。
线程:线程是程序内的一个顺序代码流,是CPU调度资源的最基本单位。是能够执行程序代码的一个执行单元,一个进程可以拥有多个线程,各个线程之间共享程序的内存空间,但是各个线程拥有自己的栈空间。

多线程标准的解释:是指从软件或者硬件上实现多个线程并发执行的技术。这里假设一个线程执行一个任务,多线程就是同时执行多个任务。举个例子:使用迅雷的时候,可以边看边播,这就需要多线程来实现。

多线程的好处:

多线程可以减少程序的响应时间,如果使用单线程,遇到等待或者阻塞i,会导致界面不响应等等,使用多线程可以解决这情况,增强线程的交互性。
线程的创建和切换开销更小,因为线程共享代码片段,数据。
目前计算机都具有执行多线程的能力,使用单线程就无法重复利用计算机资源,造成资源的浪费。
一个负责的进程可以分为多个线程去执行,简化程序结构。

多线程的缺点:
线程的死锁。即较长时间的等待或资源竞争以及死锁等多线程症状。
对公有变量的同时读或写,当多个线程需要对公有变量进行写操作时,后一个线程往往会修改掉钱=前一个线程存放的数据,从而使前一个线程的参数被修改;另外,当公有变量的读写操作是非原子性,不同机器上,中断的时间不确定性,会导致数据在一个线程内的操作产生错误,会导致莫名其妙的错误。计算的正确性取决于多个线程的交替执行时序时,就会发生竟态条件。

也就是说多线程容易引发安全问题。什么叫线程安全:在多线程和但线程执行永远都能获得一样的结果,就是线程安全。


例子:

class MyThread extends Thread {public static int number;public void run() {for (int i = 0; i < 12; i++) {System.out.println(getName() + ":" +number++);}}}public class A {//private int i = 0;////public synchronized void a(int a) {//i++;//System.out.println(i + "");////}//public synchronized void b() {//System.out.println("sdsd");//}public static void main(String[] args) {// TODO Auto-generated method stubnew MyThread().start();new MyThread().start();}}
执行结果是:
Thread-0:0Thread-1:1Thread-0:2Thread-1:3Thread-1:5Thread-1:6Thread-0:4Thread-1:7Thread-0:8Thread-1:9Thread-0:10Thread-1:11Thread-0:12Thread-1:13Thread-0:14Thread-0:16Thread-1:15Thread-0:17Thread-1:18Thread-0:19Thread-1:20Thread-0:2Thread-1:22Thread-0:23

这个执行结果是不确定性的,上面的例子,两个线程访问静态变量number,它们获取系统时间片的时刻是不确定的,所有它们对index的访问和修改总是穿插进行的。想要解决上面的情况, 也就是以某种顺序来确保该资源在某一时刻只能被一个线程使用,不然运行结果不安全。简单来讲:A线程需要使用某个资源,如果该资源正在被线程B使用,同步机制就会让A线程一直等待,直到线程B结束 对该资源的使用,线程A才能使用。要想实现同步操作,必须获得每一个线程对象的锁。实现同步有三种方式:synchronized,wait(),notify(),Lock(JDK5中加入)。下面探讨synchorized.:  

二、synchronized的用法

synchorized是解决多线程并发问题的一种方法。(1)确保线程互斥的访问同步代码 (2)保证共享变量的修改能够及时可见(3)有效解决重排序问题。一般synchronized有三种用法:
1.修饰普通方法
2.修饰静态方法
3.修饰代码块

例子:

public class A {   public void a(){   System.out.println("方法a开始执行");   for(int i = 0;i<20;i++){   System.out.println("1111");   }   System.out.println("方法a执行完毕");   }      public void b(){   System.out.println("方法b开始执行");   for(int i = 0;i<20;i++){   System.out.println("2222");   }   System.out.println("方法b执行完毕");   }public static void main(String[] args) {// TODO Auto-generated method stubA a = new A();new Thread(new Runnable() {//开启线程@Overridepublic void run() {// TODO Auto-generated method stuba.a();}}).start();new Thread(new Runnable() {//开启线程@Overridepublic void run() {// TODO Auto-generated method stuba.b();}}).start();}}

结果:

方法a开始执行
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
方法b开始执行
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
方法b执行完毕
1111
1111
1111
方法a执行完毕

可见 两个线程同时执行。

修改用synchronzied来修饰方法:

public class A {public synchronized void a() {System.out.println("方法a开始执行");for (int i = 0; i < 20; i++) {System.out.println("1111");}System.out.println("方法a执行完毕");}public synchronized void b() {System.out.println("方法b开始执行");for (int i = 0; i < 20; i++) {System.out.println("2222");}System.out.println("方法b执行完毕");}public static void main(String[] args) {// TODO Auto-generated method stubA a = new A();new Thread(new Runnable() {// 开启线程1@Overridepublic void run() {// TODO Auto-generated method stuba.a();}}).start();new Thread(new Runnable() {// 开启线程2@Overridepublic void run() {// TODO Auto-generated method stuba.b();}}).start();}}

输出结果:

方法a开始执行
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
方法a执行完毕
方法b开始执行
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
2222
方法b执行完毕


可见,线程2需要等待线程1完成后才执行。


如果创建两个不同实例来执行同一个方法呢?

public class A {public synchronized void a(int index) {System.out.println("方法a开始执行");for (int i = 0; i < 20; i++) {System.out.println("index:"+index);}System.out.println("方法a执行完毕");}//public synchronized void b() {//System.out.println("方法b开始执行");//for (int i = 0; i < 20; i++) {//System.out.println("2222");//}//System.out.println("方法b执行完毕");//}public static void main(String[] args) {// TODO Auto-generated method stubA a1 = new A();A a2 = new A();new Thread(new Runnable() {// 开启线程@Overridepublic void run() {// TODO Auto-generated method stuba1.a(10);}}).start();new Thread(new Runnable() {// 开启线程@Overridepublic void run() {// TODO Auto-generated method stuba2.a(20);}}).start();}}

输出结果:

方法a开始执行
index:10
index:10
index:10
index:10
index:10
index:10
index:10
index:10
index:10
index:10
方法a开始执行
index:10
index:20
index:10
index:20
index:10
index:20
index:10
index:20
index:10
index:20
index:10
index:10
index:10
index:10
index:20
index:10
index:20
方法a执行完毕
index:20
index:20
index:20
index:20
index:20
index:20
index:20
index:20
index:20
index:20
index:20
index:20
index:20
方法a执行完毕


可见,没有构成线程同步,那么当synchronized修饰普通方法时,如果多个线程访问一个对象的不同synchronized方法时,是可以构成同步,因为是用一个锁。当多个线程如果访问不同对象的synchronized方法时,不能构成同步,因为没有持有同一把锁。


如果是synchronized修饰静态方法?

public class A {public static synchronized void a(int index) {System.out.println("线程1:方法a开始执行");try {System.out.println("线程1:方法执行中");Thread.sleep(3000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("线程1:方法a执行完毕");}public static synchronized void b() {System.out.println("线程2:方法b开始执行");try {System.out.println("线程2:方法执行中");Thread.sleep(500);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("线程2:方法b执行完毕");}public static void main(String[] args) {// TODO Auto-generated method stubA a1 = new A();A a2 = new A();new Thread(new Runnable() {// 开启线程@Overridepublic void run() {// TODO Auto-generated method stuba1.a(10);}}).start();new Thread(new Runnable() {// 开启线程@Overridepublic void run() {// TODO Auto-generated method stuba2.b();}}).start();}}

执行结果:

线程1:方法a开始执行
线程1:方法执行中
线程1:方法a执行完毕
线程2:方法b开始执行
线程2:方法执行中
线程2:方法b执行完毕


可分析:静态方法是属于类的,而对静态方法的同步等同于对类进行同步,虽然a1和a2属于不同的对象,但它们属于synchorized类的实例,所以只能顺序执行a,b方法,不能并发执行。


那么synchorized修饰代码块?

public class A {public  void a(int index) {System.out.println("线程1:方法a开始执行");try {synchronized(this){System.out.println("线程1:方法执行中");Thread.sleep(3000);}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("线程1:方法a执行完毕");}public  void b() {System.out.println("线程2:方法b开始执行");try {synchronized(this){System.out.println("线程2:方法执行中");Thread.sleep(500);}} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("线程2:方法b执行完毕");}public static void main(String[] args) {// TODO Auto-generated method stubA a1 = new A();//A a2 = new A();new Thread(new Runnable() {// 开启线程@Overridepublic void run() {// TODO Auto-generated method stuba1.a(10);}}).start();new Thread(new Runnable() {// 开启线程@Overridepublic void run() {// TODO Auto-generated method stuba1.b();}}).start();}}


执行结果:

线程1:方法a开始执行
线程1:方法执行中
线程2:方法b开始执行
线程2:方法执行中
线程1:方法a执行完毕
线程2:方法b执行完毕


也就是两个线程都进入各自的方法去执行,但是线程2进入同步代码块时,需要线程1的同步代码块执行完才能执行。







原创粉丝点击