Java基础学习:线程篇

1.程序、进程、线程的概念

  1.1 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。

  1.2 进程(process) 是程序的一次执行过程,或是正在运行的一个程序。动态过程:有它自身的产生、存在和消亡的过程。如:运行中的QQ,运行中的MP3播放器,程序是静态的,进程是动态的。

  1.3 线程进程可进一步细化为线程,是一个程序内部的一条执行路径。若一个程序可同一时间执行多个线程,就是支持多线程的。

2.何时需要多线程?

  2.1 程序需要同时执行两个或多个任务。

  2.2 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。

  3.3 需要一些后台运行的程序时。

 3.如何实现一个多线程程序?

  3.1 继承Thread类的写法

 1 package com.usagi.thread;
 2 /**
 3  * 案例:创建一个子线程,完成1-100的输出,主线程执行同样的操作
 4  */
 5 public class TestThread {
 6     public static void main(String[] args) {
 7         SubThread subThread = new SubThread();
 8         subThread.start();
 9         System.out.println("main 已经执行完成~");
10     }
11 }
12 // 1.创建一个继承于Thread的子类
13 class SubThread extends Thread{
14     // 2.重写Thread的run方法,方法内实现此子线程要完成的功能
15     @Override
16     public void run() {
17         for (int i = 0; i < 100; i++) {
18             System.out.println(i);
19         }
20         System.out.println("子类执行完成~");
21     }
22 }

   PS:一个线程只能执行一次 start() 方法,不能通过thread实现类对象的run方法去启动。

  3.2 Thread类的常用方法

    1.start():启动线程并执行相应的run()方法

    2.run():子线程要执行的代码放入run()方法中

    3.currentThread():静态的,调取当前的线程

    4.getName():获取此线程的名字

    5.setName():设置此线程的名字

    6.yield():调用此方法的线程释放当前cpu的执行权

    7.join():在a线程中调用b线程的join方法,表示:当执行到此方法,a线程停止执行,直至b线程执行完毕,a线程再接着join之后的代码执行

    8.isAlive():判断一个线程是否继续存活

    9.sleep(Long L):显式的让当前线程睡眠L毫秒

    10.线程通信:

      wait() :令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问

      notify() : 唤醒正在排队等待同步资源的线程中优先级最高者结束等待

      notifyAll() : 唤醒正在排队等待资源的所有线程结束等待.

    11.设置线程的优先级: getPriority():返回线程优先值,setPriority():改变线程的优先级

   3.3 实现一个多窗口售票案例

 1 package com.usagi.thread;
 2 class Window extends Thread{
 3     private static Integer ticket = 100;
 4     @Override
 5     public void run() {
 6         while(true){
 7             if(ticket > 0){
 8                 System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket -- );
 9             }else{
10                 break;
11             }
12         }
13     }
14 }
15 public class TestWindow {
16     public static void main(String[] args) {
17         Window window1 = new Window();
18         Window window2 = new Window();
19         Window window3 = new Window();
20         window1.setName("窗口1");
21         window2.setName("窗口2");
22         window3.setName("窗口3");
23         window1.start();
24         window2.start();
25         window3.start();
26     }
27 }

  3.4 使用实现接口的方式实现多线程

 1 package com.usagi.thread;
 2 /**
 3  * 案例:创建一个子线程,完成1-100的输出,主线程执行同样的操作
 4  */
 5 public class TestRunnable {
 6     public static void main(String[] args) {
 7         SubRunnable subRunnable = new SubRunnable();
 8         Thread thread = new Thread(subRunnable);
 9         thread.start();
10         System.out.println("main 执行完成");
11     }
12 }
13 /**
14  * 实现Runnable 接口,重写 run 方法
15  */
16 class SubRunnable implements Runnable{
17     public void run() {
18         for (int i = 0; i < 100; i++) {
19             System.out.println(i);
20         }
21         System.out.println("子类执行完成~");
22     }
23 }

  3.5 实现的方法实现多窗口案例

 1 package com.usagi.thread;
 2 public class TestRunnableWindow {
 3     public static void main(String[] args) {
 4         RunnableWindow window = new RunnableWindow();
 5         Thread thread1 = new Thread(window);
 6         Thread thread2 = new Thread(window);
 7         Thread thread3 = new Thread(window);
 8         thread1.setName("窗口1");
 9         thread2.setName("窗口2");
10         thread3.setName("窗口3");
11         thread1.start();
12         thread2.start();
13         thread3.start();
14     }
15 }
16 class RunnableWindow implements Runnable{
17     Integer ticket = 100;
18     public void run() {
19         while(true){
20             if(ticket > 0){
21                 System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket -- );
22             }else{
23                 break;
24             }
25         }
26     }
27 }

  3.6 上面的输出1 - 100 会存在线程安全问题,如何暴露?

 1 class RunnableWindow implements Runnable{
 2     Integer ticket = 100;
 3     public void run() {
 4         while(true){
 5             if(ticket > 0){
 6                 try {
 7                     Thread.currentThread().sleep(10);
 8                 } catch (InterruptedException e) {
 9                     e.printStackTrace();
10                 }
11                 System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket -- );
12             }else{
13                 break;
14             }
15         }
16     }
17 }

 

  PS:此程序存在线程安全问题:打印车票时,会出现重票、错票。

  3.7 线程安全问题存在的原因:由于一个线程在操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据而出现了安全问题。

  3.8 如何来解决线程的安全问题:必须让一个线程操作共享数据完毕以后,其他线程才有机会参与共享数据的操作。

4.Java如何实现线程安全

  4.1 同步代码块

    synchronized(同步监视器){

      // 需要被同步的代码块(即为操作共享数据的代码)

    }

  4.2 共享数据:多个线程共同操作的同一个数据(变量)。

  4.3 同步监视器:由一个类的对象来充当。哪个线程获取此监视器,谁就执行大括号里被同步的代码。锁的机制,要求共用一把锁(可以考虑用this),在实现的方式中,考虑同步的话,可以使用this来充当锁,但是在继承的方式中,慎用this。

 1 class RunnableWindow implements Runnable{
 2     Integer ticket = 100;
 3     Object object = new Object();
 4     public void run() {
 5         while(true){
 6             //synchronized (object) {
 7 synchronized (this) {
 8                 if (ticket > 0) {
 9                     try {
10                         Thread.currentThread().sleep(10);
11                     } catch (InterruptedException e) {
12                         e.printStackTrace();
13                     }
14                     System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--);
15                 } else {
16                     break;
17                 }
18             }
19         }
20     }
21 }

 

  4.4 同步方法:将操作共享数据的方法声明为synchronized,即此方法为同步方法,能够保证其中一个线程执行此方法时,其他线程在外等待直至此线程执行完此方法。同步方法的锁:this 

 1 class RunnableWindow implements Runnable{
 2     Integer ticket = 100;
 3     public void run() {
 4         while (true){
 5             show();
 6         }
 7     }
 8     public synchronized void show(){
 9         if (ticket > 0) {
10             try {
11                 Thread.currentThread().sleep(10);
12             } catch (InterruptedException e) {
13                 e.printStackTrace();
14             }
15             System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--);
16         }
17     }
18 }

 

5.互斥锁

  5.1 在Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。

  5.2 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。

  5.3 关键字synchronized 来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。

  5.4 同步的局限性:导致程序的执行效率要降低

  5.5 同步方法(非静态的)的锁为this。

  5.6 同步方法(静态的)的锁为当前类本身。

6.死锁问题

  6.1 死锁:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。

  6.2 解决方法:专门的算法、原则;尽量减少同步资源的定义。

posted @ 2018-04-10 00:29  东特出猫  阅读(231)  评论(0编辑  收藏  举报