JAVA~线程

进程和线程的区别是啥,为什么要了解他们呐?

  • 进程:

  •  正在运行的程序,是系统进行资源分配和调用的独立单位。
    
  •  每一个进程都有它自己的内存空间和系统资源。
    
  • 线程:

  •  是进程中的单个顺序控制流,是一条执行路径
    
  •  一个进程如果只有一条执行路径,则称为单线程程序。
    
  •  一个进程如果有多条执行路径,则称为多线程程序。
    
  • 举例:

  •  QQ程序(多人聊天),迅雷下载(多个下载任务)
    
  • 大家注意两个词汇的区别:并行和并发。

  •  前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。
    
  •  后者是物理上同时发生,指在某一个时间点同时运行多个程序。
    
  • Java程序的运行原理:

  •  由java命令启动JVM,JVM启动就相当于启动了一个进程。
    
  •  接着有该进程创建了一个主线程去调用main方法。
    
  • 思考题:

  •  jvm虚拟机的启动是单线程的还是多线程的?
    
  •  	多线程的。
    
  •  	原因是垃圾回收线程也要先启动,否则很容易会出现内存溢出。
    
  •  	现在的垃圾回收线程加上前面的主线程,最低启动了两个线程,
    
  •      所以,jvm的启动其实是多线程的。
    

Thread类的基本获取和设置方法
public final String getName()
public final void setName(String name)
public static Thread currentThread()
这样就可以获取任意方法所在的线程名称

线程休眠
public static void sleep(long millis)
 
线程加入
public final void join()
线程的插队,一旦插队成功,则肯定先执行完插入的线程所有的任务.

线程礼让
public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。 
让多个线程的执行更和谐,但是不能靠它正好保证一人一次。
(让出CPU,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功)

后台线程
public final void setDaemon(boolean on)

中断线程
public final void stop()
让线程停止,过时了,但是还可以使用。中断线程。 把线程的状态终止,并抛出一个InterruptedException。
public void interrupt()

注意:

  • 如何获取线程对象的优先级?
  •  public final int getPriority():返回线程对象的优先级
    
  • 如何设置线程对象的优先级呢?
  •  public final void setPriority(int newPriority):更改线程的优先级。 
    
  •  线程默认优先级是  5。
    
  •  线程优先级的范围是: 1-10。
    
  •  线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,
    
  •  或者多次运行的时候才能看到比较好的效果。	
    
  • IllegalArgumentException:非法参数异常。
    
  • 抛出的异常表明向方法传递了一个不合法或不正确的参数。
    
  • 方式2:实现Runnable接口
  • 步骤:
  •  A:自定义类MyRunnable实现Runnable接口
    
  •  B:重写run()方法
    
  •  C:创建MyRunnable类的对象
    
  •  D:创建Thread类的对象,并把C步骤的对象作为构造参数传递
    
package 线程;

public class MyRunnable implements  Runnable{
    @Override
    public void run() {
        for(int x=0;x<100;x++){
            /*
            由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用
             */
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
    }
}

package 线程;

public class MyRunnableDemo {
    public static void main(String[] args){
        //创建MyRunnable类对象
        MyRunnable my=new MyRunnable();
        //Thread(Runnable target)
        Thread t1=new Thread(my);
        Thread t2=new Thread(my);
         t1.setName("线程一");
        t2.setName("线程二");
        /*  //Thread(Runnable target,String name)
        Thread t1=new Thread(my,"线程一");
        Thread t2=new Thread(my,"线程二");
      */
        t1.start();
        t2.start();
        
    }

}

练习

有一个售票系统,共有100张票,而它有3个售票端售票,请设计一个程序模拟售票过程。
继承Thread类来实现。


public class SellTicket extends Thread{
    private int tickets=100;
    public  void run(){
        while(true){
         
            if (tickets > 0)
            System.out.println(getName()+"正在出售第"+(tickets--)+"张票");
        }

    }
}

package 线程;
public class SellTicketDemo {
    public static void main(String[] args) {
        // 创建三个线程对象
        SellTicket st1 = new SellTicket();
        SellTicket st2 = new SellTicket();
        SellTicket st3 = new SellTicket();

        // 给线程对象起名字
        st1.setName("售票端1");
        st2.setName("售票端2");
        st3.setName("售票端3");

        // 启动线程
        st1.start();
        st2.start();
        st3.start();
    }
}

如何解决线程安全问题呢?

要解决问题,就要知道哪些原因会导致出问题:(这些原因也是以后判断程序是否有线程安全问题的标准)
A:是否是多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据

我们来回想一下我们的程序有没有上面的问题呢?
A:是否是多线程环境	是
B:是否有共享数据	是
C:是否有多条语句操作共享数据	是

由此可见我们的程序出现问题是正常的,因为它满足问题的条件。
接下来才是我们要想想如何解决问题呢?
A和B的问题我们改变不了,我们只能想办法去把C改变一下。

思想:
	把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行。
问题是我们不知道怎么包啊?其实我也不知道,但是Java给我们提供了:同步机制。

同步代码块:
synchronized(对象){
需要同步的代码;
}

	A:对象是什么呢?
		我们可以随便创建一个对象试试。
	B:需要同步的代码是哪些呢?
		把多条语句操作共享数据的代码的部分给包起来

注意:
同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
多个线程必须是同一把锁。

package 线程;
/*
用户线程和守护线程
1.用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
3.常见的守护线程:垃圾回收制
 */
public class ThreadMethod03 {
    public static void main(String[] args) throws InterruptedException {
        MyDaemonThread dt=new MyDaemonThread();
        /*
        将dt设置为守护线程,当所有线程结束后,dt也自动结束
        如果没有设置,那么即使main线程执行完毕,dt也不退出
         */
        dt.setDaemon(true);
        dt.start();
        for (int i=1;i<=10;i++){
            Thread.sleep(1000);
            System.out.println("宝强在辛苦的工作。。。。");
        }
    }
}
class MyDaemonThread extends Thread{
    @Override
    public void run() {
        for(;;){
            try {
                Thread.sleep(1000);//休眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("马蓉和经理快乐聊天,哈哈哈……");
        }
    }
}
package 线程;
/*Runnable接口实现*/
public class SellTicket implements Runnable{
    private int tickets=100;
    public  void run(){
        //创建锁对象
       Object obj=new Object();
        while(true) {
            synchronized (obj) {
                 if (tickets > 0) {

                    try {
                        Thread.sleep(100);//t1就稍作休息,t2就稍作休息

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
                }
            }
        }
    }
}

    Synchronized(线程同步机制)
    1.在多线程编程,一些敏感数据不允许被多个线程同时访问,
    此时就使用同步访问技术,在保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
    2.也可以这样子理解:线程同步,即当一个线程在对内存进行操作时,其他线程才能对该内存地址进行操作。
    3.同步具体方法-synchronized
       1)同步代码块(对象){//得到对象的锁,才能操作同步代码
       需要被同步代码;
       }
       2)synchronized还可以放在方法声明中,表示整个方法-为同步方法
       public synchronized void m(String name){
        需要被同步代码;
       }
package 线程;
/*
        Synchronized(线程同步机制)
        1.在多线程编程,一些敏感数据不允许被多个线程同时访问,
        此时就使用同步访问技术,在保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
        2.也可以这样子理解:线程同步,即当一个线程在对内存进行操作时,其他线程才能对该内存地址进行操作。
        3.同步具体方法-synchronized
           1)同步代码块(对象){//得到对象的锁,才能操作同步代码
           需要被同步代码;
           }
           2)synchronized还可以放在方法声明中,表示整个方法-为同步方法
           public synchronized void m(String name){
            需要被同步代码;
           }
 */
/*
基本介绍
1. Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
2.每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻, 只
能有一个线程访问该对象。
3.关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,
表明该对象在任一 时刻只能由一 个线程访问
4.同步的局限性:导致程序的执行效率要降低
5.同步方法(非静态的)的锁可以是this, 也可以是其他对象(要求是同一一个对象)
6.同步方法(静态的)的锁为当前类本身。

●注意事项和细节
1.同步方法如果没有使用static修饰:默认锁对象为this
2.如果方法使用static修饰,默认锁对象:当前类.class
3.实现的落地步骤:
需要先分析上锁的代码
选择同步代码块或同步方法
要求多个线程的锁对象为同一个即可!

 */
public class synchronizedTest {
    public static void main(String[] args) {
        ST3 st3=new ST3();
        new Thread(st3).start();
        new Thread(st3).start();
        new Thread(st3).start();

    }
}
class ST3 implements Runnable{
   private int tickets=100;
    private boolean loop=true;
    //同步方法(静态的)的锁为当前类本身
    //如果在静态方法中要实现一个同步代码块。
    public synchronized static  void m(){
        synchronized (synchronizedTest.class){
            System.out.println("m1");
        }
    }//锁是加在synchronizedTest。class
    /*
    说明:1.public synchronized void sell() {}就是一个同步方法
    2.这时锁在this对象
    3.也可以在代码块上写synchronized,同步代码块,互斥锁还是zaithis对象

     */
public/* synchronized */void sell() {//同步方法,在同一时刻,只能有一个线程来执行sell方法
    synchronized (this) {
     //解读
            //1.这里使用synchronized实现了线程同步
            //2、当多个线程执行到这里时,就会去争夺this对象锁
            //3、哪个线程争夺到(获取)this对象锁,就执行 synchronized 代码块,执行完后,会释放this对象锁
            //4.争夺不到this对象锁,就blocked,准备继续争夺
            //5、this对象锁是非公平锁
        if (tickets <= 0) {
            System.out.println("售票结束。。。");
            loop = false;//结束死循环
            return;
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("窗口" + Thread.currentThread().getName() + "卖了一张票,剩余票数:" + (--tickets));

    }
}
    @Override
    public void run() {
        while(loop) {
         sell();
        }
    }
}


线程终止
要求:启动一个线程t,要求在main线程中通知线程t,请编程实现

package 线程;

public class ThreadExit {
    public static void main(String[] args) {
        /*
          如果希望main线程去控制t1线程的终止,必须可以修改loop
          让t1退出run方法,从而终止t1线程-》通知方式;
         */
        
        AThread st=new AThread();
        new Thread(st).start();
        for (int i = 1; i <=60 ; i++) {
            try {
                Thread.sleep(10*1000);//让主线程休眠10秒,再通知t1线程退出
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("main线程运行中..."+i);
            if(i==30)
                st.setLoop(false);//中断线程
        }
    }
}
class AThread implements Runnable{
    boolean loop=true;//步骤一:定义标记变量,默认为true
    @Override
    public void run() {
        while(loop){//步骤二:将loop作为循环条件
            try {
                Thread.sleep(1000);//让线程休眠
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("AThread 运行中...");
        }
    }
    //步骤三:提供公共的set方法,用于更新loop
    public void setLoop(boolean loop){
        this.loop=loop;
    }
}

/线程的生命周期图是否重要!!!/

package 线程;
/*线程的生命周期图是否重要!!!*/
public class ThreadState {
    public static void main(String[] args) throws InterruptedException {
        T t=new T();
        System.out.println(t.getName()+"状态"+t.getState());
        t.start();
        while(Thread.State.TERMINATED!=t.getState()){
            System.out.println(t.getName()+"状态"+t.getState());
            Thread.sleep(1000);
        }
        System.out.println(t.getName()+"状态"+t.getState());
    }
}
class  T extends Thread{
    @Override
    public void run() {
        while(true){
            for (int i = 1; i <=10 ; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("hi "+i);
            }
            break;
        }
    }
    /*
    Thread-0状态NEW
Thread-0状态RUNNABLE
Thread-0状态TIMED_WAITING
hi 1
Thread-0状态TIMED_WAITING
hi 2
Thread-0状态TIMED_WAITING
hi 3
Thread-0状态TIMED_WAITING
hi 4
Thread-0状态TIMED_WAITING
hi 5
Thread-0状态TIMED_WAITING
hi 6
Thread-0状态TIMED_WAITING
hi 7
Thread-0状态TIMED_WAITING
hi 8
Thread-0状态TIMED_WAITING
hi 9
Thread-0状态TIMED_WAITING
hi 10
Thread-0状态TERMINATED

进程已结束,退出代码0
*/
}

编程题1
(1)在main方法中启动两个线程
(2)第一个线程循环随机打印100以内的整数
(3)直到第二个线程从键盘读取了"Q"命令

package 线程;
import java.util.Random;
import java.util.Scanner;

import static java.lang.Math.*;

public class Homework01 {
    public static void main(String[] args) {
        A a = new A();
        B b = new B(a);//一定要注意
        new Thread(a).start();
        new Thread(b).start();


    }
}
class A implements  Runnable{
    private  boolean loop=true;

    @Override
    public void run() {
        while(loop){
            System.out.println((int)(Math.random()*100));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


        }
    }
    public void setLoop(boolean loop) {
        this.loop = loop;
    }
}
class B implements Runnable{
   private A a;
    public B(A a) {//构造器中,直接传入A类对象
        this.a = a;
    }
    Scanner sc=new Scanner(System.in);
    @Override
    public void run() {
        while(true){
            //接收用户的输入
            System.out.println("请输入你的指令(Q)表示退出:");
            char c=sc.next().toUpperCase().charAt(0);
            if(c=='Q'){
                //以通知的方式结束A线程
                a.setLoop(false);
                System.out.println("B线程退出");
                break;
            }
        }

    }
}

编程题2
Homework02.java 5min
(1)有2个用户分别从同一个卡上取钱(总额: 10000)
(2)每次都取1000,当余额不足时,就不能取款了
(3)不能出现超取现象=》线程同步问题.(blocked机制)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值