多线程

1. 进程和线程

1.1 进程

进程就是电脑中一个运行的软件,一个正在运行的程序

在做的一件事情就是一个进程

1.2 线程

线程理解为一个进程中步骤

我们在做一件事情时,分很多步骤,每一个步骤就是一个线程,同一时刻只能做一件事情

做菜是一个进程,买菜,洗菜,切菜,炒菜就是一个一个的线程。

注意:

进程间不能共享数据段地址,但同进程的线程之间可以。

  • QQ软件不能使用谷歌内存中数据,360不能使用QQ内存中的数据。
  • 同一个进程中的线程,可以贡献数据,会产生数据不安全的情况。

1.3 进程是如何执行的

java 只有一个进程是怎么使用多核的 java多进程写同一个文件_java 只有一个进程是怎么使用多核的

 

java 只有一个进程是怎么使用多核的 java多进程写同一个文件_java 只有一个进程是怎么使用多核的_02

 

2. 创建线程

2.1 继承Thread类

class 类 extends Thread {

    public void run() {
        //这个线程被cpu选中执行时,执行的业务代码
    }
}
  • run方法是使用当前类创建线程,被cpu选中时,实行的业务代码
package com.qfedu;

public class Demo01 {

    //main方法    就是一个线程(主线程)
    public static void main(String[] args) throws InterruptedException {
        
        MyThread mt = new MyThread();
        mt.start();   // 开启了一个线程,和主线程共同竞争cpu执行时间
        
        
        for (int i = 0; i < 100; i++) {
            System.out.println("main--"+i);
        }
        
    }
    
}

/*
 * 创建线程
 * 1. 创建类继承Thread
 * 2. 重写run方法              该线程执行的业务代码
 */
class MyThread extends Thread {
    
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("自定义线程--"+i);
        }
    }
    
}

2.2 实现Runnable接口

实现Runnable接口,重写run方法

class 类 implements Runnable {

    public void run() {
        //这个线程被cpu选中执行时,执行的业务代码
    }
}

Thread t = new Thread(类的对象);
package com.qfedu;

public class Demo02 {

    public static void main(String[] args) {
        
        Runnable runnable = new YourThread();
        
        Thread t = new Thread(runnable, "线程1");
        Thread t2 = new Thread(runnable, "线程2");
        
        t.start();    // 开启了一个线程,和主线程共同竞争cpu执行时间
        t2.start();   // 开启了一个线程,和线程1共同竞争cpu执行时间
    }
}

/*
 * 实现Runnable接口
 * 重写run方法              该线程执行的业务代码
 */
class YourThread implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"--"+i);
        }
    }
    
}

3. 线程的状态

基本状态:

新建状态:创建线程对象

就绪状态:线程对象执行start()方法

运行状态:被cpu选中,执行run方法,如果在分配的时间片内运行结束,进入死亡状态,如果没有运行 结束,回到就绪状态。

死亡状态:run方法执行结束,进入死亡状态

java 只有一个进程是怎么使用多核的 java多进程写同一个文件_servlet_03

 

package com.qfedu;

public class Demo03 {

    public static void main(String[] args) {
        
        //1. 新建状态
        System.out.println("新建状态");
        HerThread mt = new HerThread();
        
        /*
         * 2. 就绪状态      
         * 
         * 这个线程可以和这个进程中的其他线程,共同竞争cpu的运行时间
         * 
         * 如果竞争到了,就执行它的run方法中代码     会进入运行状态
         * - 如果在运行的时间片段内,run方法没有执行结束,那么就回到就绪状态
         * - 如果run方法执行结束,该线程就over了
         */
        System.out.println("就绪状态");
        mt.start();
    }
    
}

class HerThread extends Thread {
    
    @Override
    public void run() {
        System.out.println("运行状态");
        
        System.out.println("死亡状态");
    }
    
}

4. 卖票实例

  • 四个窗口各卖100张票
  • 四个窗口共卖100张票
package com.qfedu;

public class Demo04 {

    /*
     * 四个窗口,每个窗口各卖100张票
     */
    public static void main(String[] args) {
        MyTicket t1 = new MyTicket("窗口1");
        MyTicket t2 = new MyTicket("窗口2");
        MyTicket t3 = new MyTicket("窗口3");
        MyTicket t4 = new MyTicket("窗口4");
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        
    }
}

class MyTicket extends Thread {
    
    private int count = 100;
    
    public MyTicket(String name) {
        super(name);
    }
    
    public void run() {
        
        while(true) {
            
            if(count <= 0) {
                break;
            }
            
            System.out.println(Thread.currentThread().getName()+"卖了,第"+(100 - --count)+"张票");
            
        }
        
    }
    
    
}
package com.qfedu;

public class Demo04_2 {

    /*
     * 四个窗口,共卖100张票
     */
    public static void main(String[] args) {
        Runnable runnable = new YourTicket();
        
        Thread t1 = new Thread(runnable, "窗口1");
        Thread t2 = new Thread(runnable, "窗口2");
        Thread t3 = new Thread(runnable, "窗口3");
        Thread t4 = new Thread(runnable, "窗口4");
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        
    }
    
}

class YourTicket implements Runnable {
    
    private int count = 100;

    @Override
    public void run() {
        
        while(true) {
            
            if(count <= 0) {
                break;
            }
            System.out.println(Thread.currentThread().getName()+"卖了,第"+(100 - --count)+"张票");
            
        }
        
    }
    
}

5. 线程常用方法


//休眠,啥都不干1000毫秒,进入等待状态,1000毫秒后回到就绪状态
public static native void sleep(long millis) throws InterruptedException;
//加入,随机就变成了顺序,什么t线程执行结束,主线程才会回到就绪状态
public final void join() throws InterruptedException {}

//主动放弃cpu资源,回到就绪状态
public static native void yield();
//设置线程调用优先级  1-10   默认是5
public final void setPriority(int newPriority) {}
//是否是守护线程    非守护线程执行结束   守护线程自动结束
public final void setDaemon(boolean on) {}


  • 线程在执行sleep(long millis) , join() 后进入等待状态
  • 线程在执行yield()后,回到就绪状态
package com.qfedu;

public class Demo05 {

	public static void main(String[] args) throws InterruptedException {
		
		for(int i=1; i<100; i++) {
			
			/*
			 * 休眠,啥都不干1000毫秒,进入等待状态,1000毫秒后回到就绪状态
			 * 
			 * 在等待的过程中,是不释放锁的
			 */
			Thread.sleep(1000);
			
			System.out.println("吃西瓜,现在是第" + i +"个");
		}
		
	}
	
}
package com.qfedu;

public class Demo06 {

    /*
     * 30个桃子
     * 
     * 20个西瓜
     * 
     * 开始随机的吃,如果桃子已经吃了10个,还有西瓜,那就先把西瓜吃完,才能吃桃子
     * 
     * 
     * 随机  改成  顺序
     */
    public static void main(String[] args) throws InterruptedException {
        
        EatWatermelon t = new EatWatermelon();
        t.start();
        
        
        for(int i=1; i<=30; i++) {
            System.out.println("吃了第"+i+"个桃子");
            
            if(i == 10) {
                 //加入,随机就变成了顺序,什么t线程执行结束,主线程才会回到就绪状态
                t.join();  
            }
            
        }
        
    }
    
}

//吃西瓜线程
class EatWatermelon extends Thread {
    
    @Override
    public void run() {
        
        for(int i=1; i<=20; i++) {
            System.out.println("吃了第"+i+"个西瓜");
        }
        
    }
    
}
package com.qfedu;

public class Demo07 {

    public static void main(String[] args) {
        
        YieldThread yt = new YieldThread();
        yt.start();
        
        for(int i=1; i<=100; i++) {
            
            System.out.println("main--"+i);
            
            if(i%3 == 0) {
                Thread.yield(); // 如果是3的倍数,就主动放弃cpu资源,回到就绪状态
            }
        }
    }
}

class YieldThread extends Thread {
    
    @Override
    public void run() {
        
        for(int i=1; i<=100; i++) {
            
            System.out.println("YieldThread--"+i);
            
            if(i%8 == 0) {
                yield();  // 如果是8的倍数,就主动放弃cpu资源,回到就绪状态
            }
            
        }
    }
    
}
package com.qfedu;

public class Demo08 {

    public static void main(String[] args) {
        DaemonThread t1 = new DaemonThread();
        t1.setName("非守护线程");
        t1.setPriority(10);
        
        DaemonThread t2 = new DaemonThread();
        t2.setName("守护线程");
        t2.setDaemon(true);
        t2.setPriority(1);
        
        t1.start();
        t2.start();
    }
}

class DaemonThread extends Thread {
    
    @Override
    public void run() {
        for(int i=1; i<=100; i++) {
            System.out.println(getName() + "--"+i);
        }
    }
}

6. 线程安全

一个进程中的多个线程在执行的过程中可能会对一个变量进行操作,这样就有可能出现数据混乱的问题,可以通过对程序加锁来解决这种情况。


synchronized (对象) { //代码块 }


  • 被加锁的代码,在同一时刻只能由一个线程执行
  • 想要执行加锁的代码,必须 获取 对象的锁
package com.qfedu;

public class Demo09 {

    public static void main(String[] args) {
        
        Runnable runnable = new HerTicket();
        
        Thread t1 = new Thread(runnable, "窗口1");
        Thread t2 = new Thread(runnable, "窗口2");
        Thread t3 = new Thread(runnable, "窗口3");
        Thread t4 = new Thread(runnable, "窗口4");
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}


class HerTicket implements Runnable {
    
    private int count = 100;
    
    Object o = new Object();

    @Override
    public void run() {
        
        while(true) {
            
            //对代码块进行加锁,这个代码块在一个时刻,只能由一个线程执行
            synchronized (o) {
                if(count <= 0) {
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"卖了,第"+(100 - --count)+"张票");
            }
            
            
        }
        
    }
    
}