多线程

进程:一个正在执行的程序

            每一个进程执行都有一个执行程序,该顺序是一个执行路径,或者叫一个控制单元

线程:进程中的一个独立控制单元.

            线程在控制着进程的执行

            一个进程中至少有一个线程

JAVA VM 启动的时候会有一个进程 java.exe

该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于 main 方法中

该线程称之为主线程,主线程执行 main()方法中的代码。

     多线程 (举例:迅雷下载)

扩展:JVM启动不止一个线程,还有负责垃圾回收机制的线程


如何在自定义代码中,自定义一个线程??  (Thread 类)

创建新线程第一种方式:继承Thread 类

步骤:

1、定义类继承Thread

2、重写Thread类的run()方法;

    目的:将自定义的代码存储在run()方法中,让线程运行。

3、调用线程 start()方法:该方法两个作用:启动线程;调用run()方法;

 

每次运行结果都不同,因为多个线程都获取CPU的执行权,CPU执行到谁,谁就运行。

   某一个时刻,只能有一个程序在运行。(多核除外,多核的瓶颈为内存大小)

CPU在做着快速的切换,以达到看上去是同时运行的效果。

可以形象的把多线程的运行行为看做是互相抢夺CPU的执行权。

多线程特性;随机性。谁抢到谁执行,至于执行多长,CPU说的算。

 

为什么要覆盖 run() 方法?

Thread 类用于描述线程,该类定义了一个功能,用于存储线程要运行的代码,该存储功能的就是

run() 方法 ,也就是说Thread 类中的run()方法,用于存储线程要运行的代码。


public class ThreadDemo {
	public static void main(String[] args) {
		Demo d = new Demo();    //创建一个线程
		d.start();               //start()方法,启动线程并执行该线程的run()方法
        d.run();                //仅仅是对象调用,线程创建了,但是并没有运行
		for(int i = 0; i < 50; i++)
		System.out.println("Hello World--" + i);
	}
}

class Demo extends Thread {
	public void run() {
		for(int i = 0; i < 50; i++)
		System.out.println("Demo Run --" + i);
	}
}
线程的运行状态:

获取线程对象及名称:

线程都有自己的名称,Thread-编号为线程的默认名称,通过调用 getName()获得;

Static ThreadcurrentThread() :获取当前线程对象;

getName():获取线程名称;

设置线程名称:setName() 或者使用super()调用父类构造函数;

 

创建线程的第二种方式:实习Runable接口

步骤:

1、  定义类实现Runable接口

2、  覆盖Runable接口中的run()方法

        将线程要运行的代码存放在run()方法中。

3、  通过Thread类建立线程对象

4、  Runable接口的子类对象作为实际参数传递给Tread类的构造函数

   为什么要将Runable接口的子类对象传递给Thread的构造函数。

   因为自定义的run()方法所属的对象是Runable接口的子类对象

   所以要让线程去调用指定对象的run()方法,就必须明确该run()方法所属对象。

5、  调用Thread类的start()方法开启并调用Ruanable接口子类的run()方法;

 

实现方式和继承方式有什么区别?

实现方式好处:避免了单继承的局限性。

在定义线程时,建议使用实现方式;

 

两种方式区别:

继承Thread :线程代码存放在Thread子类run()方法;

实现Runable,线程代码存放在接口的子类run()方法中。

//多个窗口同时售票
class Ticket implements Runable               //定义类实现Runable 接口
{
	private int tick = 100;

	public void run()             ///重写run()方法
	{
		while(true)
		{
			if(tick > 0)
			{
				System.out.println(Thread.currentThread().getName() + "sale```" + tick--);
			}
		}
	}
	
}

public class TicketDemo 
{
	
	public static void main(String[] args) 
	{
		Ticet t = new Ticket();
		Thread t1 = new Thread(t);    //通过Thread 类建立线程对象
		Thread t2 = new Thread(t);
	  Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		t1.start();         //通过Thread类的star()方法开启线程,并调用Runable接口子类的run()方法
		t2.start();
		t3.start();
		t4.start();
	}

}

多线程运行中的安全问题:

问题的原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完

另一个线程参与进来执行,导致共享数据的错误。

   解决办法:对多条操作共享数据的语句,只能让一个线程执行完毕,在执行过程中,其他线程不可以参与执行。


Java对于多线程的安全问题提供的专业解决方式:同步代码块。

Synchronized(对象)

{

   需要被同步的代码

}

对象如同锁,持有锁的线程可以在同步中执行,

没有持有锁的线程即使获取了CPU的执行权也进不去,因为没有获取锁。


同步的前提:

1、  必须要有两个或者两个以上的线程。

2、  必须是多个线程使用同一个锁。

必须保证同步中只能有一个线程在运行。

好处:解决了多线程的安全问题;

弊端:多个线程都需要判断锁,较为消耗资源。

class Ticket implements Runnable
{
	private int tick = 100;
          Object obj = new Object()
	public void run() 
	{
		while(true)
		{
			synchronized(obj)
			{
				if(tick > 0)
				{
					try
					{
						Thread.sleep(10);
					}catch(InterruptedException e) 
					{
						e.printStackTrace();
					}     
					System.out.println(Thread.currentThread().getName() + "-----sale-----" + tick--);
				}
			}
		}
	}
	
}

/*需求分析:银行有一金库
  有2个储户同时往里面存300,每次存100,分3次*/
 
如何找问题: 
  1、明确哪些代码是多线程运行代码;
  2、明确共享数据;
  3、明确多线程运行代码中哪些语句是操作共享数据的;
class Bank 
{
	public int sum;
	Object obj = new Object();
	public void add(int n) 
	{
		synchronized(obj)
		{
			sum = sum + n;
			try
			{
				Thread.sleep(20);
			}catch(InterruptedException e)
			{
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"--sum = " + sum);	
		}
	}
	
}

class Customer implements Runnable
{
	private Bank b = new Bank();
	public void run()
	{
		for(int i=0; i<8; i++)
		{
			b.add(100);
		}
	}
	
}

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

同步函数用的是哪一个锁?
同步函数需要被对象调用,那么函数都有一个所属对象引用,就是this
所以同步函数使用的锁是this
class Ticket implements Runnable
{
private int tick = 100;
  Object obj = new Object();
  boolean flag = true;
	public void run() 
	{
		if(flag)
		{
			while(true)
			{
				synchronized(this)
				{
					if(tick > 0)
					{
						try
						{
							Thread.sleep(100);
						}catch(InterruptedException e) 
						{
							e.printStackTrace();
						}     
						System.out.println(Thread.currentThread().getName() + "-----code-----" + tick--);
					}
				}
			}
		}
		else
			while(true)
			{
				show();
			}
	}
	
	public synchronized void show()
	{
		if(tick > 0)
		{
			try
			{
				Thread.sleep(10);
			}catch(InterruptedException e) 
			{
				e.printStackTrace();
			}     
			System.out.println(Thread.currentThread().getName() + "-----show-----" + tick--);
		}
	}
	
}
如果同步函数被静态修饰后,使用的锁是什么?
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码对象,
类名.class,该对象的类型是class.
静态的同步方法,使用的锁是该方法所在类的字节码文件对象,即 类名.class
class Ticket implements Runnable
{
	private 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(100);
						}catch(InterruptedException e) 
						{
							e.printStackTrace();
						}     
						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(InterruptedException e) 
			{
				e.printStackTrace();
			}     
			System.out.println(Thread.currentThread().getName() + "-----show-----" + tick--);
		}
	}
	
}

/*单例设计模式
*/
//饿汉式
class Single
{
	private static final Single s = new Single();
	private Single() {}
	pbulic static Single getInstance()
	{
		return s;
	}
}

//懒汉式
class Single
{
	private static Single s = null;
	private Single() {}
	public static Single getInstance()
	{
		if(s==null)
		{
			synchronized(Single.class)
			{
				if(s==null)
				{
					s = new Single();
				}
			}
		}
		return s;
	}
}

面试:懒汉式和饿汉式有什么不同?

懒汉式的特点用于实例的延迟加载;

懒汉式延迟加载的问题:如果多线程访问时会出现安全问题,可以用加同步

来解决,而加同步的方式用同步代码块和同步函数都可以,但是稍微有些

低效,用双重否定的形式可以解决效率问题,加同步时使用的锁为该类所属的

字节码对象,即 类名.class

/*死锁:同步中嵌套同步,而锁却不同*/
class Test implements Runnable
{
	private boolean flag = true;
	 Test(boolean _flag)
	{
		this.flag = _flag;
	}
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(MyLock.locka)
				{
					System.out.println("if locka");
					synchronized(MyLock.lockb)
					{
						System.out.println("if lockb");
					}
				}
			}
		}
		else
		{
			while(true)
			{
				synchronized(MyLock.lockb)
				{
					System.out.println("else lockb");
					synchronized(MyLock.locka)
					{
						System.out.println("else locka");
					}
				}
			}
		}
	}
}

class MyLock
{
	static Object locka = new Object();
	static Object lockb = new Object();
}
public class DeadLockDemo
{
	public static void main(String[] args)
	{
		
		Thread t1 = new Thread(new Test(true));
		Thread t2 = new Thread(new Test(false));
		t1.start();
		t2.start();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值