您的位置:首页 > 职场人生

黑马程序员——java语言基础部分——线程

2015-05-22 18:13 429 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

概述:

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

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

   注意:

一个进程中至少有一个线程。
Java VM  启动的时候会有一个进程java.exe。该进程中至少一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。
jvm启动不止一个线程,还有负责垃圾回收机制的线程。

创建线程方式

1、继承Thread类

子类徐覆盖类中的run方法,将线程运行的代码存放在run中

 简历子类对象的同时线程也被创建

通过调用start方法开启线程

2、实现Runnable接口

子类覆盖接口中的run方法

通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数

Thread类对象调用start方法开启线程

Thread类

   常用方法:

获取:

long getId() 返回该线程的标识符。
String getName() 返回该线程的名称。
int getPriority() 返回线程的优先级。
Thread.State getState()
返回该线程的状态。
String toString() 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

判断:

static boolean interrupted() 测试当前线程是否已经中断。
boolean isAlive() 测试线程是否处于活动状态。
boolean isInterrupted() 测试线程是否已经中断。

中断:

void interrupt() 中断线程。
static void yield() 暂停当前正在执行的线程对象,并执行其他线程。

加入:

void join()等待该线程终止。
void join(long millis)等待该线程终止的时间最长为 millis 毫秒。
void join(long millis, int nanos)等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
注意:假如当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行

设置:

void setName(String name)
改变线程名称,使之与参数 name 相同。
void setPriority(int newPriority)更改线程的优先级。
void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)设置该线程由于未捕获到异常而突然终止时调用的处理程序。

休眠:

static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
static void sleep(long millis, int nanos)在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

运行:

   void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

线程的状态

     线程有四种状态:创建,运行,冻结,消亡

     创建——》运行:start()方法

     运行——》冻结:sleep(),interrupt()

     冻结——》运行:notify(),notifyAll()

     总结:notify()唤醒的是对应的中断的线程,notifyAll()唤醒的是所有处于冻结状态的线程。

线程安全

     原因:

多个线程访问出现延迟

线程随机性。

          解决方案:同步

同步

     同步前提:

两个或者两个以上的线程

多个线程使用的是同一个锁

     同步弊端:线程多时,对于同步上锁的判断会消耗很多的哦资源,降低运行效率

     同步格式:

       Synchronized(对象){

       同步代码

     }

    同步的关键,控制共享的部分,即锁。

    同步的锁:

   同步代码的锁是object

   同步函数的锁是this。因为函数的调用者是对象。

   静态的同步函数的锁是类的字节码文件。因为静态函数在方法区中,是随着类的加载而加载的。

    关于同步的思考思路总结:

        明确哪些代码是多线程运行代码。

        明确共享数据。

        明确多线程运行代码中哪些语句是操作共享数据的。

死锁

      即两个线程,各自持有各自的资源互不相让。因为只有获取了所需的资源和CPU才能执行程序。   

      参考代码

class Ticket implements Runnable
{
private  int tick = 1000;
Object obj = new Object();
boolean flag = true;
public  void run()
{
if(flag)
{
while(true)
{
synchronized(obj)
{
show();
}
}
}
else
while(true)
show();
}
public synchronized void show()//this
{
synchronized(obj)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
}
}
}
}

class  DeadLock
{
public static void main(String[] args)
{

Ticket t = new Ticket();

Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);//让main线程沉睡一会
}catch(Exception e){}
t.flag = false;
t2.start();

}
}


Condition和Lock

         接口Condition:

     Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

     Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。

          常用方法:

void await() 造成当前线程在接到信号或被中断之前一直处于等待状态。
void signal() 唤醒一个等待线程。
void signalAll() 唤醒所有等待线程。

      类 ReentrantLock

    一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
    ReentrantLock 将由最近成功获得锁,并且还没有释放该锁的线程所拥有。当锁没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁并返回。如果当前线程已经拥有该锁,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。
    此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。否则此锁将无法保证任何特定访问顺序。与采用默认设置(使用不公平锁)相比,使用公平锁的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢),但是在获得锁和保证锁分配的均衡性时差异较小。不过要注意的是,公平锁不能保证线程调度的公平性。因此,使用公平锁的众多线程中的一员可能获得多倍的成功机会,这种情况发生在其他活动线程没有被处理并且目前并未持有锁时。还要注意的是,未定时的
tryLock 方法并没有使用公平设置。因为即使其他线程正在等待,只要该锁是可用的,此方法就可以获得成功。

         获取锁的方法是: void lock() 
         获取condition实例方法: Condition newCondition()。
     使用lock机制的参考代码:

/*作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。*/

class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull  = lock.newCondition();
final Condition notEmpty = lock.newCondition();

final Object[] items = new Object[100];
int putptr, takeptr, count;

public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}

public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}


单例模式

     参考代码

*/
//饿汉式
class Single
{
private static final Single s = new Single();
private Single(){}
public 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)
//--->A;
s = new Single();
}
}
return s;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息