一、并发编程产生的问题
并发编程给程序的设计带来的巨大的好处,但多线程的出现也同样带来了一些问题。
因为线程执行的时间是不确定的,所以,当不同的线程共同访问某一共享资源的时候,可以对共享资源的操作产生错误。
比如:有一共享资源整型num,如果有几个线程同时对其做减1操作。假设当num=90时,线程一对其做减一操作,其从内存中取出num=90,然后到cpu进行减法操作。
这个时候,线程一挂起,线程二开始执行,线程二从内存中取出num=90,对其进行减一操作,num变为了89,。而线程切换到线程一的时候,线程一继续执行,在cpu中做减法操作,num变为89。
也就是执行了两次减一操作,当时共享资源num只是从90变为了89.
二、如何解决这一问题——互斥量操作
1、为了保护资源,采用了加锁的方式。当资源被一个任务使用的时候,对其进行加锁操作。只用当前任务完成,才释放锁。下一个任务才能访问该资源。
在java中使用synchronized关键字的形式控制资源的访问。
2、共享资源。在java中共享资源一般是以对象的形式存在内存片段的。但也可以是文件,输入输出端口,或者是打印机。
3、synchronized的使用
(1)将方法标记为synchronized,如果某个任务处于一个标记为synchroenized的方法的调用中,那么在这个线程从该方法返回之前,其他所有要调用类中任何标记为synchronized方法的线程都会被阻塞。
(2)临界区
有时,希望防止多个线程同时访问方法内部的部分代码,而不是防止访问整个方法。
通过这种方法,分离出来的代码段称为临界区,它使用synchronized关键字建立。
synchronized被用来指定某个对象,此对象的锁被用来对花括号内的代码进行同步控制。
synchronized(对象)
{
需要控制的代码!
}
这种方法叫做同步控制块,通过同步控制块,而不是对整个方法进行同步控制,可以使得多个任务访问对象的时间性能得到显著提高。
使用一对象,作为锁
package com.xiancheng;
class task1 implements Runnable
{
private int num = 100;
Object o = new Object();
public void run()
{
while(num>0)
{
synchronized(o)
{
if(num>0)
{
try
{
Thread.sleep(10);
}
catch(InterruptedException e)
{
}
System.out.println(Thread.currentThread().getName()+"........"+num--);
}
}
}
}
}
class task2 implements Runnable
{
private int num = 100;
// Object o = new Object();
public void run()
{
while(num>0)
{
synchronized(this)
{
if(num>0)
{
try
{
Thread.sleep(10);
}
catch(InterruptedException e)
{
}
System.out.println(Thread.currentThread().getName()+"........"+num--);
}
}
}
}
}
class task2 implements Runnable
{
public void run()
{
for(int z=0;z<=10;z++)
{
for(int y=0;y<=99999999;y++){}
System.out.println(Thread.currentThread().getName()+"....z="+z);
}
}
}
public class hello {
public static void main(String[] args){
task1 t1= new task1();
task2 t2 = new task2();
Thread nt1 = new Thread(t1);
Thread nt2 = new Thread(t1);
Thread nt3 = new Thread(t1);
nt1.start();
nt2.start();
nt3.start();
}
}