多线程1(java基础)

一、什么是多线程?

1、进程

      进程是指正在运行的程序,但是cpu执行的并不是进程而是线程。

2、线程

      线程是进程内一个相对独立的、可调度的执行单元或控制单元。操作系统可执行的最小单位是线程。一个进程中至少一个线程。

3、多线程

      线程在操作系统中是可以并发运行的,这样可以充分利用外围设备。以java.exe为例,该进程至少包含两个线程,一个是执行代码的线程,另一个是回收和释放内存的线程。他们是并发执行的,我们称一个进程中多个线程运行的为多线程。

4、多线程的意义(多线程与cpu的关系)

      目前我们常用的操作系统为分时系统,这类操作系统的特点是在极短时间内在不同的程序间切换执行,让我们看起来就像是很多程序同时运行。而多线程的存在就可以提升cup的切换速度,提高执行效率。这里需注意的是cpu在切换线程执行是随机的,具体下面有实例


二、线程的几种状态

1、就绪:线程获取了除cpu意外的全部资源,等待CPU的调度。

2、执行:线程获得CPU执行权限,正在执行。

3、挂起(休眠):因为终端请求或者操作系统要求,线程停止运行,被挂起。挂起的线程不能自己苏醒,必须被其他线程唤醒。

4、阻塞:线程因为I/O等操作导致无法运行,转到就绪状态。

5、消亡:stop()或者进程结束。

三、java的两种线程创建方式

1、将类声明为 Thread 的子类(继承的方式)

      创建方法:

      1)定义一个demo类继承Thread类。
      2)复写Thread类的run方法。
         理由:run中存放的是自定义运行的代码
      3)调用线程的start方法。
         作用:1.启动线程 ;2.调用run方法。

程序示例:

package exam1;

/* 创建两个线程,让其与主程序交替运行。
 * 1)定义一个demo类继承Thread类。
 * 2)复写Thread类的run方法。
 *   理由:run中存放的是自定义运行的代码
 * 3)调用线程的start方法。
 *   作用:1.启动线程 ;2.调用run方法。
 */

class demo extends Thread//定义一个demo类继承Thread类。
{
    //private String name;
    demo(String name)
    {
        //this.name = name;
        super(name);
    }
    public void run()//复写Thread类的run方法,其中主要存放线程需要执行的代码
    {
        for(int i=0; i<60;i++)
//        System.out.println(name+"  run!!---"+i);
            //currentThread() 获取当前线程的对象
            //getName()获取当前线程的名称
        System.out.println(this.currentThread().getName()+"run!--"+i);
    }
}

public class Test1
{

    public static void main(String[] args)
    {
        demo t1 = new demo("one++++");
        demo t2 = new demo("two----");
//        t1.run();//直接调用方法,并不新建一个进程
//        t2.run();//
        t1.start();//调用线程的start方法,新建一个进程并在进程中调用方法。
        t2.start();
        
        
        for(int i = 0; i < 100; i++)
        System.out.println("Main---!!"+i);
    }
}


执行结果:


从结果中可以看出:同一个进程的几个线程在执行过程中,是随机交替执行的。

2、声明实现 Runnable 接口的类

 * 步骤:
 * 1.定义类实现Runnable接口。
 * 2.覆盖Runnable接口中的RUN方法。
 * 3.通过Thread类建立线程对象。
 * 4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

package exam1;

/*
 * 需求:简单的卖票程序。(多线程,实现Runnable接口)
 *    多窗口同时买票。
 *    
 *    
 * 步骤:
 * 1.定义类实现Runnable接口。
 * 2.覆盖Runnable接口中的RUN方法。
 * 3.通过Thread类建立线程对象。
 * 4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
 * 
 */

class Ticket implements Runnable//实现Runnable接口
{
    private int Tick = 100;
    public void run()
    {
        while(Tick>0)
        {
            System.out.println(Thread.currentThread().getName()+"  run---"+Tick--);
        }
    }
}


public class Test2
{    

    public static void main(String[] args)
    {
        Ticket T = new Ticket();
        Thread t1 = new Thread(T);
        Thread t2 = new Thread(T);
        Thread t3 = new Thread(T);
        Thread t4 = new Thread(T);
        Thread t5 = new Thread(T);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }

}
/*---继承的方式购票系统--*/
/*class Ticket extends Thread
{
    private static int Tick = 100;//
    public void run()
    {
        while(Tick>0)
        {
            System.out.println(currentThread().getName()+"....."+Tick--);
        }
    }
}


public class Test2
{    

    public static void main(String[] args)
    {
        Ticket T1 = new Ticket();
        Ticket T2 = new Ticket();
        Ticket T3 = new Ticket();
        Ticket T4 = new Ticket();
        Ticket T5 = new Ticket();
        T1.start();
        T2.start();
        T3.start();
        T4.start();
        T5.start();

    }

}*/

两种方式的联系和区别:继承方式的局限性是继承了Thread类用来创建线程,那么这个类就无法继承其他的类。而通过实现Runnable接口创建线程不存在这个限制。定义线程时候最好采用实现方式,避免java单继承的局限性。

四、安全问题

在运行上面的售票程序的时候出现了卖出了0号,-1号等错误的票。


1、出现上述问题的原因是当一个进程执行共享的数据时候,该语句执行一部分就停止了然后切换到了另一线程。这就是著名的生产者,消费者问题。

2、解决方案。

     同步函数

       格式:

                在函数上加上synchronized修饰符即可。

        那么同步函数用的是哪一个锁呢?

        函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this。



1)同步代码块。

     示例:
     *  synchronized(object)
     *   {
     *       需要同步的代码块。
     *   }

     生产者消费者解释:参考http://blog.chinaunix.net/uid-21411227-id-1826740.html

package exam1;

/*
 * 需求:简单的卖票程序。(多线程,实现Runnable接口)
 *    多窗口同时买票。
 *    
 *    
 * 步骤:
 * 1.定义类实现Runnable接口。
 * 2.覆盖Runnable接口中的RUN方法。
 * 3.通过Thread类建立线程对象。
 * 4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
 * 注:run方法是存放线程运行代码的位置。
 */


/* 多线程出现安全问题:
 * 产生原因:多个线程共享一个数据,且是多条语句操作时候。一个线程对多条语句只执行了一部分,并没有执行完,
 * 此时另一个线程参与进来。
 * 
 * 解决办法:对多条操作共享数据的语句,一个线程在执行此语句时候其他线程不允许操作。
 *       JAVA提供的解决方案:同步代码块。
 *       示例:
 *       synchronized(object)
 *       {
 *       需要同步的代码块。
 *       }
 *       解释:火车上的厕所,进去一个人将锁锁上。这里的synchronized(object)就相当于厕所门。
 *       同步的好处:解决了多线程的安全问题。
 *       弊端:线程每次执行都会判断一次锁,比较消耗资源。
 * 
 */

class Ticket implements Runnable//实现Runnable接口
{
    private int Tick = 100;
    Object obj = new Object();//new一个同步代码块需要的对象
    public void run()
    {
        while(true)
        {
            synchronized(obj)//同步代码块
            {
                if(Tick > 0)
                {
                try{Thread.sleep(10);}catch(Exception e){}//使用sleep()模拟真实情况,测试安全问题
                System.out.println(Thread.currentThread().getName()+"  sale---"+Tick--);
                }
            }
        }
        
    }
}

     2)同步函数

     示例:

             public synchronized void function(参数)
              {
              }

              跟同步代码块一样,同步函数也有一个所属对象引用,也就是一个锁(this)。

     代码:

package exam1;

/* 需求: 银行金库。
 * 有两个用户分别存300元,每次存100元,存三次。
 *
 * 目的:验证此程序有无安全问题?且如何解决。
 * 
 * 如何找到问题?
 * 1. 哪些带代码是多线程运行代码?
 * 2. 哪些是共享数据?
 * 3. 哪些语句是操作共享数据的?
 */

class Bank
{
	private int sum;//金库的钱的总数。 
	public synchronized void add(int m)//同步函数
	{
//		synchronized (this)//同步代码块解决
//		{
		sum = sum + m;
		try{Thread.sleep(10);}catch(Exception e){}//停10ms模拟
		System.out.println("sum="+sum);
//		}
	}
}
class Cus implements Runnable
{
	private Bank b = new Bank();//新建一个名为b的Bank对象。
	public void run()
	{
		for(int i=0; i<3; i++)
		{
			b.add(100);
		}
	}
}

public class Test3
{

	public static void main(String[] args)
	{
		Cus c = new Cus();
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		t1.start();
		t2.start();

	}

}

五、静态函数的同步。

     同步函数被静态修饰后,所属对象引用不是this,所以锁不再是this了,此时内存中虽然没有本类对象,但是一定有该类对应的字节码对象。

     即:类名.class

     代码:

   

package exam1;
/*
 * 如果同步函数被 静态修饰后,使用的锁是什么呢?(不是this)
 * 此时该对象的类型为 类名.class,使用的锁是该方法所在类的字节码文件对象。
 */

class Ticket2 implements Runnable//实现Runnable接口
{
	private static int Tick = 100;
//	Object obj = new Object();//new一个同步代码块需要的对象
	boolean flag = true;
	public void run()
	{
		while(true)
		{
			synchronized(Ticket2.class)//静态的同步方法
			{
				if(Tick > 0)
				{
			    try{Thread.sleep(10);}catch(Exception e){}//使用sleep()模拟真实情况,测试安全问题
				System.out.println(Thread.currentThread().getName()+"  sale---"+Tick--);
				}
			}
		}
		}

	}
} 

六、单例设计模式。

//懒汉式,作用是延迟加载.这里很重要。
class Single
{
    private static Single s = null;
    private Single(){}
    public static Single getInstance()//此处因为s是共享的数据,而对其操作的是多条语句。
    {
        synchronized(Single.class)//同步代码块
        {
        if(s == null)
            s = new Single();
        }
        return s;
    }
}


代码:

package exam1;


/*@ 面试中恶汉式和懒汉式的区别。
 *@ 
 *@
 */

/*
 * 单例设计模式
 */

//恶汉式

/*
class Single
{
	private static final Single s = new Single();//final更加严谨
	private Single(){}
	public static Single getInstance()
	{
		return s;
	}
} 
*/

//懒汉式,作用是延迟加载。
class Single
{
	private static Single s = null;
	private Single(){}
	public static Single getInstance()//此处因为s是共享的数据,而对其操作的是多条语句。
	{
		synchronized(Single.class)//同步代码块
		{
		if(s == null)
			s = new Single();
		}
		return s;
	}
}

public class Test6 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}

七、死锁

两个进程同时持有自己的锁,然后去获取对方的锁,导致相互永远等待的情况,这称为死锁。

代码:

package exam1;

class Test_8 implements Runnable///实现Runnable接口(端口实现多线程)
{
	private boolean flag;
	Test_8(boolean flag)
	{
		this.flag = flag;
	}
	public void run() {
		if(flag)
		{
			
			synchronized(Mylock.locka)//a锁内嵌套b锁
			{
				System.out.println("if locka");
				synchronized(Mylock.lockb)
				{
					System.out.println("if lockb");
				}
			}
		}
		else
		{
			synchronized(Mylock.lockb)//b锁内嵌套a锁
			{
				System.out.println("if lockb");
				synchronized(Mylock.locka)
				{
					System.out.println("if locka");
				}
			}
		}	
	}
}

class Mylock//存储两个锁。
{
    static Object locka = new Object();
    static Object lockb = new Object();
}


public class Test8 {

	public static void main(String[] args) {
		Thread t1 = new Thread(new Test_8(true));
		Thread t2 = new Thread(new Test_8(false));
		t1.start();
		t2.start();
	}

}

运行结果:

这里看到锁上了,两个线程都持有对应的锁,且要获取对方的锁。导致程序锁死。


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值