黑马程序员 java多线程
------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------
1.进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径或者叫一个控制单元。
2.线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行。
3.一个进程中至少有一个线程。
4.java VM 启动的时候会有一个进程java.exe,该进程中至少有一个线程负责java程序的执行,而且这个线程进行的代码存在于main方法中,该线程称之为主线程。(扩展:其实jvm启动不止一个线程,还有负责垃圾回收机制的线线程)
5.创建线程的方式一:
(1)继承Thread类,创建子类对象的同时就创建了线程,创建多少个子类对象就有多少条线程。
步骤:a.定义类继承Thread。
b.复写Thread类中的run方法。目的:将自定义代码存储在
run方法。让线程运行。
c.调用线程的start方法。该方法两个作用:启动线程;
调用 run方法。
d.要是只调用run方法,这仅仅是调用对象的方法,而没有启动线程。
(2)多线程的运行其实是在互相抢cpu的执行权。所以多线程有一个特性:随机性。
(3)Thread类中的run方法用于存储线程要运行的代码。
(4)线程运行状态:运行、临时状态、冻结:sleep(time)时间到了就会醒;wait(等待)需要被notify(唤醒)才会醒、消亡(stop();run方法结束)。
(5)获取线程名称:因为定义类继承Thread,所以直接用this.getName调用,线程都有自己默认的名称:Thread-编号,编号从0开始。
(6)改变线程名称:子类构造函数中可以调用父类的super(name);
(7)Thraed.currentThraed().getName:获取当前线程对象的名称(通用)
6.创建线程的方式二:实现Runnable接口
步骤:
a.定义类实现Runnable接口
b.覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。
c.通过Thread类建立线程对象
d.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去指定对象的run方法。Ticket t=new Ticket();
Thread t1=new Thread(t)
(1)实现方式和继承方式的区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable,线程代码存放接口的子类的run方法。实现方式的好处是避免了单继承的局限性。
7.多线程的运行出现安全问题。
(1)问题的原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
(2)解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
(3)如何找问题:
明确哪些代码是多线程运行代码;明确共享数据;明确多线程运行代码中哪些语句是操作共享数据的。
(4)多线程的安全问题解决方式:同步代码块。
synchronied(对象)
{需要被同步的代码}
(5)同步的前提:
A.必须要有两个或者两个以上的线程。
B.必须是多个线程使用同一个锁。
C.必须保证同步中只能有一个线程在运行
(6)弊端:多个线程需要判断锁,较为消耗资源。
8.同步函数的锁是synchronized(this);静态同步函数的锁是
synchronized(类名.class)
9.多线程-单例设计模式
懒汉式的特点是延迟加载实例,在多线程访问时,会出现安全问题,可以使用同步代码块或同步函数来解决,但比较低效,可以通过双重判断的形式来解决低效问题。同步所使用的锁是该类字节码文件对象。具体示例如下:
//多线程单例设计模式—懒汉式
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;
}
}
10.死锁
//死锁示例
class DeadLockTest
{
public static void main(String[] args)
{
Thread t1=new Thread(new Test(true));//创建线程并操作Test类中的run方法
Thread t2=new Thread(new Test(false));
t1.start();//启动线程
t2.start();
}
}
class Test implements Runnable
{
private boolean flag;
//建立构造函数初始化Test对象
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();
}
11.线程间通信:其实就是多个线程在操作同一个资源,但是操作的动作不同。
12.public final void wait() throw InterruptedException
Wait方法抛出了异常,用的时候要try和catch。
13.wait(),notify(),notifyAll()只能在同步里面用。因为要对持有锁的线程操作,而只有同步才具有锁,所以要使用在同步中。
14.为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。不可以对不同锁中的线程进行唤醒。也就是说等待和唤醒必须是同一个锁,所以要:对象.wait,对象.notify。
15.而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
16.DK1.5中提供了多线程升级解决方案,将同步Synchronized替换成显示的Lock操作。将Object中的wait,notify,notifyAll,替换了Condition对象。
//生产者与消费者JDK5.0升级版示例
import java.util.concurrent.locks.*;
class ProducerConsumerDemo
{
public static void main(String[] args)
{
//将类实例化
Resource r=new Resource();
Producer pro=new Producer(r);
Consumer con=new Consumer(r);
//创建线程并操作对应类中的run方法
Thread t1=new Thread(pro);
Thread t2=new Thread(pro);
Thread t3=new Thread(con);
Thread t4=new Thread(con);
//启动线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource
{
private String name;
private int count=1;
private boolean flag=false;
private Lock lock=new ReentrantLock();//创建锁
//lock.newCondition()返回一个Condition对象
private Condition condition_pro=lock.newCondition();
private Condition condition_con=lock.newCondition();
public void set(String name)throws InterruptedException
{
lock.lock();//调用锁
try
{
while(flag)
condition_pro.await();//等待
this.name=name+"-----"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;
condition_con.signal();//唤醒对方
}
finally
{
lock.unlock();//解锁
}
}
public void out()throws InterruptedException
{
lock.lock();
try
{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+"...消费者......."+this.name);
flag=false;
condition_pro.signal();
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res=res;
}
public void run()
{
while(true)
{
try
{
res.set("+商品");
}
catch (InterruptedException e)
{
}
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res=res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch (InterruptedException e)
{
}
}
}
}
17.停止线程
定义循环结束标记:因为线程运行代码一般都是循环,只要控制了循环即可。
使用interrupt(中断)方法:该方法是结束线程的交结状态,使线程回到运行状态中来。
注:stop方法已经过时不再使用。
18.守护线程:setDaemon(boolean on),该方法必须在启动线程前调用。守护线程依赖于主线程,当主线程运行结束,守护线程也跟着结束。例子:
Thread t=new Thread(d);
t.setDaemon(true);
t.start();
19.join方法
Thread t1=new Thread(d);
Thread t2=new Thread(d);
t1.start();
t1.join();
t2.start();
1线程夺取执行权,2线程和主线程冻结,先运行完1线程,然后2线程和主线程争夺执行权,交替运行。
Thread t1=new Thread(d);
Thread t2=new Thread(d);
t1.start();
t2.start();
t1.join();
1线程和2线程争夺执行权,交替运行,主线程冻结,直到1线程运行完毕,2线程如果没运行完,它会跟主线程争夺执行权,交替运行。
20.优先级
t1.setPriority(Thread.MAX_PRIORITY);最高优先级
t2.setPriority(Thread.MIN_PRIORITY);最低优先级
t3.setPriority(Thread.NORM_PRIORITY);默认优先级