您的位置:首页 > 其它

【传智播客郑州校区分享】线程精进指南

2017-09-27 09:43 507 查看

线程精进指南

文/传智播客郑州中心就业服务部

多线程的起步

• 线程和进程的区别

– 线程是程序执行的一条路径, 一个进程中可以包含多条线程

• 多线程的并行和并发

– 多线程并发执行可以提高程序的效率, 可以同时完成多项工作

– 我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行。

– 如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊。这就叫并发。

• 在生活中的表现

– 一边听歌
,一边看技术文章

– 使用迅雷下载电影

• 在JVM虚拟中的表现

– 使用虚拟机时的入口main方法是一个线程,以及我们的垃圾回收机制也是一个线程

• 在源码中的表现

– fuck the source code

 public class Thread implements Runnable {

   //省略部分源码

    ...

    //省略部分构造

    //线程是通过构造传入Runnable对象

    public Thread(Runnable target) {

        //通过调用init方法 传入target

        init(null, target,
"Thread-" +
nextThreadNum(), 0);

    }

      

     private void init(ThreadGroup g, Runnable target, String name,

                      long stackSize, AccessControlContext acc) {

        if (name ==
null
) {

            throw new NullPointerException("name cannot be null");

        }

       

        ...

        //把tager赋给成员变量

        this.target = target;

      

        setPriority(priority);

    }

  //自己重写的Runable实现的方法 ,如果外部传入target则使用外部的

  @Override

  public void run() {

        if (target !=
null
) {

            target.run();

        }

    }

  

}

• 分析源码可知如下结论

– 看Thread类的构造函数,传递了Runnable接口的引用

– 通过init()方法找到传递的target给成员变量的target赋值

– 查看run方法,发现run方法中有判断,如果target不为null就会调用Runnable接口子类对象的run方法

 

线程的进击

• 重要的方法

– Thread.sleep(毫秒,纳秒) 线程睡眠(时间)

  public static void sleep(long millis,
int nanos) throws InterruptedException {

    //省略部分代码     

    ...  

        sleep(millis);

 }
//调用本地sleep方法

 public static native void sleep(long millis)
throws InterruptedException;

– setDaemon() 守护线程,当其他线程都结束时执行此线程

   
public final void setDaemon(boolean on)
{

        checkAccess();

   //通过调用isAlive判断是否有存活线程

        if (isAlive()) {

            throw new IllegalThreadStateException();

        }

        daemon = on;

    }

/**

     * Tests if this thread is alive.
A thread is alive if it has

     * been started and has not yet died.

     *

     * @return
 <code>
true</code> if this thread is alive;

     *          <code>false</code> otherwise.

     */

    public final native boolean isAlive();

• join() 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续

  public final synchronized void join(long millis)
throws InterruptedException {

        long base = System.currentTimeMillis();

        long now =
0;

        if (millis <
0) {

            throw new IllegalArgumentException("timeout value is negative");

        }

   //通过wait(0)死循环 让线程永远处于等待状态

        if (millis ==
0) {

            while (isAlive()) {

                wait(0);

            }

        } else {

            while (isAlive()) {

                long delay = millis - now;

                if (delay <=
0) {

                    break;

                }

                wait(delay);

                now = System.currentTimeMillis() - base;

            }

        }

    }

• yield() 礼让线程 但不是绝对的,取决于cpu的时机

   /**

     * A hint to the scheduler that the current thread is willing to yield

     * its current use of a processor.
The scheduler is free to ignore this

     * hint.

     *

     * <p> Yield is a heuristic attempt to improve relative progression

     * between threads that would otherwise over-utilise a CPU. Its use

     * should be combined with detailed profiling and benchmarking to

     * ensure that it actually has the desired effect.

     *

     * <p> It is rarely appropriate to use this method. It may be useful

     * for debugging or testing purposes, where it may help to reproduce

     * bugs due to race conditions. It may also be useful when designing

     * concurrency control constructs such as the ones in the

     * {@link
java.util.concurrent.locks} package.

     */

    public static native void yield();

• 继承的Object方法

 wait() 方法导致当前线程等待,直到其他线程调用该同步监视器的notify()方法或notifyAll()方法唤醒该线程,调用wait方法会释放对当前同步监视器的锁定  notify() 唤醒在此同步监视器上等待的单个线程,如果所有线程都在此同步监视器上等待,则会唤醒其中一个线程,选择是任意的,只有当前线程放弃对同步监视器的锁定后(即调用wait()方法),才可以执行被唤醒的线程  notifyAll() 唤醒在此同步监视器上等待的所有线程,只有当前线程放弃对同步监视器的锁定后,才可以执行被唤醒的线程

线程的进阶

• 1.什么情况下需要同步

– 当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.

– 如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.

• 2.同步代码块

– 使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块

– 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的

 class Printer {

  public static void print1() {

          synchronized(Printer.class){

            //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象

              System.out.print("黑");

              System.out.print("马");

              System.out.print("程");

              System.out.print("序");

              System.out.print("员");

              System.out.print("\r\n");

          }

      }

      /*
       * 非静态同步函数的锁是:this
       * 静态的同步函数的锁是:字节码对象
       */

      public static synchronized void print2()
{

          System.out.print("传");

          System.out.print("智");

          System.out.print("播");

          System.out.print("客");

          System.out.print("\r\n");

      }

}

线程安全问题

• 多线程并发操作同一数据时, 就有可能出现线程安全问题

• 使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作

 

 public class DemoSynchronized {

/**
* @param args
* 需求:铁路售票,一共100张,通过四个窗口卖完.
*/
public static void main(String[] args) {

TicketsSeller t1 = new TicketsSeller();

TicketsSeller t2 = new TicketsSeller();

TicketsSeller t3 = new TicketsSeller();

TicketsSeller t4 = new TicketsSeller();

t1.setName("窗口1");

t2.setName("窗口2");

t3.setName("窗口3");

t4.setName("窗口4");

t1.start();

t2.start();

t3.start();

t4.start();

}

}

class TicketsSeller
extends
 Thread {
private static int tickets =
100;
static Object obj = new Object();
public TicketsSeller() {
super();

}
public TicketsSeller(String name) {
super(name);

}
public void run() {
while(true) {
synchronized(obj) {
if(tickets <=
0)
break;
try {

Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(getName() +
"...这是第" + tickets-- +
"号票");

}

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: