Java基础总结多线程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础总结多线程相关的知识,希望对你有一定的参考价值。

1. java中实现多线程的两种方式

 1 //第一种:继承Thread类,重写run()方法
 2 class ThreadTest1 extends Thread{
 3     public void run(){
 4         String threadName = Thread.currentThread().getName();
 5         for(int i=0;i<10;i++){
 6             System.out.println("ThreadTest1 "+threadName+" running ... "+i);
 7         }
 8     }
 9 }
10 
11 //第二种:实现Runnable接口,重写run()方法
12 class ThreadTest2 implements Runnable{
13     public void run(){
14         String threadName = Thread.currentThread().getName();
15         for(int i=0;i<10;i++){
16             System.out.println("ThreadTest2 "+threadName+" running ... "+i);
17         }
18     }
19 }
20 
21 public class Thread01{
22     public static void main(String[] args){
23         ThreadTest1 t1 = new ThreadTest1();
24         ThreadTest1 t2 = new ThreadTest1();
25         Thread t3 = new Thread(new ThreadTest2());  //注意
26         Thread t4 = new Thread(new ThreadTest2());
27         
28         //start() 启动线程
29         t1.start();
30         t2.start();
31         t3.start();
32         t4.start();
33     }
34 }

归根结底,两种方法都是用Thread类或其子类对象调用start()方法启动线程。

为什么用start()方法而不用run()方法启动线程呢?因为start()方法产生了运行这个线程所需的系统资源,安排其运行,并调用线程体(run()方法)。

一个线程只能启动一次,不管调用多少次start()方法,结果也只用一个线程。

2. 线程共享资源

建议实现Runnable接口,其好处是:

  • 多线程之间可以共享资源
  • 避免单继承带来的问题
  • 数据和代码独立
 1 class ThreadTest2 implements Runnable{
 2     private int count=20;    //共享资源
 3     public void run(){
 4         String threadName = Thread.currentThread().getName();
 5         while(count>0){
 6             System.out.println("ThreadTest2 "+threadName+" 售出 "+count+" 号票");
 7             count--;
 8         }
 9     }
10 }
11 
12 public class ThreadDemo2{
13     public static void main(String[] args){
14         ThreadTest2 t2 = new ThreadTest2();
15         (new Thread(t2,"窗口1")).start();
16         (new Thread(t2,"窗口2")).start();
17         (new Thread(t2,"窗口3")).start();
18         (new Thread(t2,"窗口4")).start();    
19     }
20 }

运行结果:

ThreadTest2 窗口1 售出 20 号票
ThreadTest2 窗口1 售出 19 号票
ThreadTest2 窗口3 售出 20 号票
ThreadTest2 窗口2 售出 20 号票
ThreadTest2 窗口3 售出 17 号票
ThreadTest2 窗口1 售出 18 号票
ThreadTest2 窗口1 售出 14 号票
ThreadTest2 窗口4 售出 19 号票
ThreadTest2 窗口1 售出 13 号票
ThreadTest2 窗口3 售出 15 号票
ThreadTest2 窗口3 售出 10 号票
ThreadTest2 窗口2 售出 16 号票
ThreadTest2 窗口2 售出 8 号票
ThreadTest2 窗口3 售出 9 号票
ThreadTest2 窗口1 售出 11 号票
ThreadTest2 窗口4 售出 12 号票
ThreadTest2 窗口1 售出 5 号票
ThreadTest2 窗口3 售出 6 号票
ThreadTest2 窗口2 售出 7 号票
ThreadTest2 窗口3 售出 2 号票
ThreadTest2 窗口1 售出 3 号票
ThreadTest2 窗口4 售出 4 号票
ThreadTest2 窗口2 售出 1 号票

问题:多个窗口售出了相同编号的票,发生了访问冲突。

3. 线程同步

同步synchronized意:协同步调,按照先后顺序进行;同步代码块;同步函数

前提:2个或2个以上的线程;使用同一把锁
作用:保证同步中只有一个线程在运行。
好处:解决线程安全问题
弊端:多个线程需要判断锁,消耗资源

在Java里面,同步锁的概念就是这样的。任何一个Object Reference都可以作为同步锁。我们可以把Object Reference理解为对象在内存分配系统中的内存地址。
    
解决方案:
1)同步代码块
    synchronized(类或对象)
    {
        需要同步的代码段
    }
2)同步函数
    (非static的情况)
    public synchronized void fun()
    {
        代码段
    }
    等价于
    public void fun()
    {
        synchronized(this)
        {
            代码段
        }
    }
    调用此同步函数的对象作为此同步函数的同步锁。
    
    (static的情况)
    public static synchronized void fun()
    {
        代码段
    }
    静态变量或静态方法加载到内存中时,内存中没有本类对象,但一定有了该类对应的
    字节码文件(类名.class),该对象的类型是class。静态同步函数使用的同步锁是所在类
    的字节码文件。        

 

 1 class ThreadTest implements Runnable
 2 {
 3     private int ticket=50;
 4     public void run(){
 5         while(ticket>0){        
 6             String threadName = Thread.currentThread().getName();
 7             //同步代码块(越小越好)
 8             synchronized(this){
 9                 if(ticket>0){
10                     System.out.println(threadName + " sales ticket "+ticket);
11                     ticket--;
12                 }
13             }
14         }
15     }
16 }
17 
18 public class Thread03{
19     public static void main(String[] args){
20         ThreadTest t = new ThreadTest();
21         (new Thread(t, "窗口A")).start();
22         (new Thread(t, "窗口B")).start();
23         (new Thread(t, "窗口C")).start();
24         (new Thread(t, "窗口D")).start();
25     }
26 }

运行结果:

技术分享
窗口A sales ticket 50
窗口D sales ticket 49
窗口D sales ticket 48
窗口D sales ticket 47
窗口D sales ticket 46
窗口D sales ticket 45
窗口D sales ticket 44
窗口D sales ticket 43
窗口D sales ticket 42
窗口D sales ticket 41
窗口D sales ticket 40
窗口D sales ticket 39
窗口D sales ticket 38
窗口D sales ticket 37
窗口D sales ticket 36
窗口D sales ticket 35
窗口D sales ticket 34
窗口D sales ticket 33
窗口D sales ticket 32
窗口D sales ticket 31
窗口D sales ticket 30
窗口D sales ticket 29
窗口D sales ticket 28
窗口D sales ticket 27
窗口D sales ticket 26
窗口D sales ticket 25
窗口D sales ticket 24
窗口D sales ticket 23
窗口D sales ticket 22
窗口D sales ticket 21
窗口D sales ticket 20
窗口D sales ticket 19
窗口D sales ticket 18
窗口D sales ticket 17
窗口D sales ticket 16
窗口D sales ticket 15
窗口D sales ticket 14
窗口D sales ticket 13
窗口D sales ticket 12
窗口D sales ticket 11
窗口D sales ticket 10
窗口D sales ticket 9
窗口D sales ticket 8
窗口D sales ticket 7
窗口D sales ticket 6
窗口D sales ticket 5
窗口D sales ticket 4
窗口D sales ticket 3
窗口D sales ticket 2
窗口D sales ticket 1
View Code

 

4. 线程死锁

死锁现象即相互等待的局面
(操作系统中的死锁) 指2个或多个进程在执行过程,因竞争资源而造成一种互相等待的局面,若无外力干涉,进程无法推进下去。
发生死锁的原因一般是两个对象的锁相互等待造成的。
那么为什么会产生死锁呢?

  1. 因为系统资源不足。
  2. 进程运行推进的顺序不合适。
  3. 资源分配不当。

产生死锁的条件有四个:

  1. 互斥条件:所谓互斥就是进程在某一时间内独占资源。
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

 

5. 单例模式

  特点:1.私有构造函数 2.在类中创建一个指向自己实例的私有静态引用 3.以自己实例为返回类型值的静态公有方法。

//饿汉式
class Singleton{
    private static final Singleton singleInstance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return singleInstance;
    }
}
//懒汉式
class Singleton{
    private static Singleton singleInstance =null;
    private Singleton(){}
    public static synchronized Singleton getInstance(){
        if(singleInstance==null){
            singleInstance = new Singleton();
        }
        return singleInstance;
    }
}

 

6. 线程通信(wait()和notify()、notifyAll())

  线程的状态

  技术分享

7. ReentrantLock和Condition 

技术分享View Code

8. 停止线程的方法(interrupt()和isInterrupt())

9. 守护线程和join方法

  守护线程是为其他线程提供便利服务的,当全部的用户线程结束后,守护线程才会随JVM结束工作。 thread.setDaemon(true); 

public static void main(String[] args){    
    Thread t3 = new Thread(test2, "线程t3");
    t3.start();
    t3.join();
    //主线程就此陷入等待,直到t3线程结束。
}

 

10. 线程优先级和yield方法

线程的优先级从低到高:1-10,优先级高的的优先执行,每个新线程都继承了父线程的优先级,常量:Thread.MIN_PRIORITY 值为1,Thread.MAX_PRIORITY 值为10,Thread.NORM_PRIORITY 值为5

void setPriority(priority)
int getPriority()

  yield()方法将线程从执行状态变成就绪状态。

以上是关于Java基础总结多线程的主要内容,如果未能解决你的问题,请参考以下文章

java基础知识总结--多线程

Java基础第八天听课总结--多线程&Thread

[Java] 多线程基础详细总结,附加详细实例

8-2高级基础总结 多线程,网络编程,Java基础补充

多线程(模拟买票)-----java基础知识总结

Java 基础知识点 笔记总结