进程:一个正在执行的程序
每一个进程执行都有一个执行程序,该顺序是一个执行路径,或者叫一个控制单元
线程:进程中的一个独立控制单元.
线程在控制着进程的执行
一个进程中至少有一个线程
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
<SPAN style="COLOR: #000000">/*死锁:同步中嵌套同步,而锁却不同*/
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();
}
}
</SPAN>
---------------------- Android开发、java培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net