多线程基础一

目录

一、线程基础:

1、程序:

2、进程:

3、线程:

4、进程和线程的关系:

5、JVM进程:

二、线程

1、现成的基本概念:

(1)单线程:单线程就是进程中只有一个线程。单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。

(2)多线程:由一个以上的线程组成的程序称为多线程程序。Java中,一定是从主线程开始执行(main方法),然后在主线程的某个位置创建并启动新的线程。

2、多线程的创建:

(1)继承Thread类(线程子类):

(2)实现Runnable接口(线程执行类):

(3)实现Callable接口(有返回值),允许子线程返回结果,抛出异常

(4)创建线程池:

3、线程常用的方法:

(1)setName()方法: 给线程设置名字

(2)setPriority()方法:设置线程优先级

(3)Thread.sleep()方法:线程的休眠:

(4)interrupt()方法:中断线程

(5)yield()方法:让出线程:当前线程愿意让出CPU给其他线程使用。

(6)join()方法:插队:

(7)守护线程(Daemon Thread):当其他的非守护线程执行完毕后,守护线程会陆陆续续结束

三、多线程的同步

1、锁:

2、同步锁:

3、多线程的数据不一致:

4、synchronized 关键字的用法:

(1)修饰实例方法:

(2)修饰静态代码块:

(3)修饰代码块:


一、线程基础:

1、程序:

        是指含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,可以理解为程序是包含静态代码的文件。例如:浏览器软件、音乐播放器软件等软件的安装目录和文件。

2、进程:

       进程是程序的一次执行过程,是系统运行程序的基本单位。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着, 同时,每个进程还占有某些系统资源如 CPU时间,内存空间,文件,输入输 出设备的使用权等。

3、线程:

        某些进程内部还需要同时执行多个子任务。例如,我们在使用WPS时,WPS可以让我们一边打字,一边进行拼写检查,同时还可以在后台进行自动保存和上传云文档,我们把子任务称为线程。线程是进程划分成的更小的运行单位。

4、进程和线程的关系:

         一个进程可以包含一个或多个线程,但至少会有一个主线程。

5、JVM进程:

   JVM进程启动时,自动创建Heap(堆区)Metaspace(元空间,JDK1.8以前叫Method Area方法区)。

         多个线程共享JVM进程的Heap(堆区)Metaspace(元空间)资源,但每个线程有自己的程序计数器虚拟机栈本地方法栈。系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,因此,线程也被称为轻量级进程。

二、线程

1、现成的基本概念:

(1)单线程:单线程就是进程中只有一个线程。单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。
public class SingleThread {
  	public static void main(String[] args) {
          for (int i = 0; i < 10000; i++) {
              System.out.print(i + " ");
          }
  	}
}
(2)多线程:由一个以上的线程组成的程序称为多线程程序。Java中,一定是从主线程开始执行(main方法),然后在主线程的某个位置创建并启动新的线程。
public class MultiThread {
	public static void main(String[] args) {
        // 创建2个线程
		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run() {
				for (int i = 0; i < 10000; i++) {
					System.out.println("线程1:" + i + " ");
				}
			}
		});
		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run() {
				for (int i = 0; i < 10000; i++) {
					System.out.println("线程2:" + i + " ");
				}
			}
		});

        // 启动2个线程
		t1.start();
		t2.start();
	}
}

2、多线程的创建:

(1)继承Thread类(线程子类):

          需要创建一个Thread的子类,重写run方法,再去创建子类对象,调用start()方法启动。

public class Demo01 {
    public static void main(String[] args) {
        MyThread t1=new MyThread();
        t1.start();

        for (int i = 0; i <10 ; i++) {
            System.out.println("main线程执行任务"+i);
        }
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <10 ; i++) {
            System.out.println(getName()+"子线程执行任务"+i);
        }
    }
}
(2)实现Runnable接口(线程执行类):

         需要创建Runnable的实现类,重写run方法 ,创建Runnable的实现类对象r1 ,创建Thread类的对象,将r1作为构造方法的参数进行传递 ,调用start方法启动线程。


public class Demo02 {
    public static void main(String[] args) {
        MyRun m1=new MyRun();
        Thread r1=new Thread(m1);
        r1.start();

        for (int i = 0; i <10 ; i++) {
            System.out.println("main执行任务"+i);
        }
    }
}
class MyRun implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i <10 ; i++) {
            System.out.println("子线程执行任务"+i);
        }
    }
}
(3)实现Callable接口(有返回值),允许子线程返回结果,抛出异常

         创建Callable的实现类,重写call()方法(有返回值) ,创建Callable的实现类对象 ,创建FutureTask对象,用来进行结果的管理操作 ,创建Thread类的队象 ,启动线程。

public class Demo03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCall m1=new MyCall(1,10);
        FutureTask<Integer> ft=new FutureTask<>(m1);
        Thread t1 = new Thread(ft);
        t1.start();
        System.out.println("子线程1和为:"+ft.get());

        MyCall m2=new MyCall(1,100);
        FutureTask<Integer> ft2=new FutureTask<>(m2);
        Thread t2 = new Thread(ft2);
        t2.start();
        System.out.println("子线程2和为:"+ft2.get());
    }
}
class MyCall implements Callable<Integer> {
    int begin,end;

    public MyCall(int begin, int end) {
        this.begin = begin;
        this.end = end;
    }
    @Override
    public Integer call() throws Exception {
        int number=0;
        for (int i = begin; i <=end ; i++) {
            number+=i;

        }
        return number;
    }
}
(4)创建线程池:
public class Demo04 {
    public static void main(String[] args) {
        //线程池: 使用线程池创建对象、
        ExecutorService es= Executors.newCachedThreadPool(); //创建无上限个
        es.execute(new MyRun());
        es.execute(new MyRun());

    }
}
class MyRun implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <10 ; i++) {
            System.out.println("子线程执行任务"+i);
        }
    }
}

3、线程常用的方法:

(1)setName()方法: 给线程设置名字
package com.yuan.threadMethed;

public class Demo01 {
    public static void main(String[] args) {
        //给线程设置名字
        //setName()方式设置线程ming
        //调用有参构造设置线程名
        MyThread t1 = new MyThread();
        t1.setName("土豆");
        t1.start();
        MyThread t2=new MyThread("洋芋");
        t2.start();

        //runnale作为参数
        Runnable r1 = new Runnable(){
            @Override
            public void run() {
                System.out.println("当前线程"+Thread.currentThread().getName()+"线程正在启动");
                try {
                    Thread.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("当前线程"+Thread.currentThread().getName()+"线程正在运行");
            }
        };
        Thread t3=new Thread(r1,"西红柿");
        //t3.setName("西红柿");
        t3.start();
    }
}
(2)setPriority()方法:设置线程优先级
package com.yuan.threadMethed;

public class Demo02 {
    public static void main(String[] args) {
        //创建线程1
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 'a'; i <'z' ; i++) {
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }
        },"线程一");
        t1.setPriority(1);

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <26 ; i++) {
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }
        },"线程二");
        t2.setPriority(10);

        t1.start();
        t2.start();

        //11获取线程统先级
        //System.out.println(t1.getName()+"优先级为"+t1.getPriority());
        System.out.println(Thread.currentThread().getName()+"优先级为"+ Thread.currentThread().getPriority());
    }
}
(3)Thread.sleep()方法:线程的休眠:

在线程中,可以通过调用Thread.sleep(long millis),强迫当前线程按照指定毫秒值休眠。

public class Main {

    public static void main(String[] args) {
        System.out.println("main start...");
        
        Thread t = new Thread() {
            public void run() {
                System.out.println("thread run...");
                try {
                    Thread.sleep(10); // 子线程休眠10毫秒
                } catch (InterruptedException e) {}
                System.out.println("thread end.");
            }
        };
        t.start();
        
        try {
            Thread.sleep(20); // 主线程休眠20毫秒
        } catch (InterruptedException e) {}
        System.out.println("main end...");

    }
}
(4)interrupt()方法:中断线程
package com.yuan.threadMethed;

public class Demo05 {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main线程进入");
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                long starTime=System.currentTimeMillis();
                System.out.println("进入t1线程");
                try {
                    Thread.sleep(1000*10);
                } catch (InterruptedException e) {
                    System.out.println("中断t1线程,消耗时间为:"+(System.currentTimeMillis()-starTime));
                    e.printStackTrace();
                    return;
                }
                System.out.println("结束t1线程,消耗时间为:"+(System.currentTimeMillis()-starTime));
            }
        },"线程1");
        t1.start();

        //让主线程休眠
        Thread.sleep(1000*3);
        System.out.println("main线程结束");
        // main主线程修改t1线程的中断状态=true
        // t1线程检测中断状态=true,则抛出InterruptedException,于线程执行结束
        t1.interrupt();
    }
}
(5)yield()方法:让出线程:当前线程愿意让出CPU给其他线程使用。
public static void main(String[] args) {
    // 创建子线程1:打印字母A-Z
    Thread thread1 = new Thread() {
        public void run() {
            for (char c = 'A'; c <= 'Z'; c++) {
                System.out.println(c);
            }
        }
    };

    // 创建子线程2:打印数字65-90
    Thread thread2 = new Thread() {
        public void run() {
            Thread.yield(); // 让当前线程让出CPU
             for (int c = 65; c <= 90; c++) {
                System.out.println(c);
                // Thread.yield(); // 让当前线程让出CPU
            }
        }
    };

    // 启动子线程
    thread1.start();
    thread2.start();
}
(6)join()方法:插队:

          t.join()方法会使当前线程( 主线程 或者调用t.join()的线程 )进入等待池,并等待 线程t 执行完毕后才会被唤醒。此时,并不影响同一时刻处在运行状态的其他线程。

public class Main {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("主线程Main:开始执行,即将创建并调用子线程");

        // 创建并启动子线程
        MyThread myThread = new MyThread();
        myThread.start();

        // 主线程调用myThread子线程的join()方法
        myThread.join(); // 子线程插队,插入到当前线程main的执行序列前
        
        System.out.println("主线程Main:当子线程myThread执行完毕后,主线程Main再执行");
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("子线程:开始执行");
        int sencondValue = (int)(Math.random()*1000);
        try {
            Thread.sleep(sencondValue);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("子线程:结束执行,耗时"+sencondValue+"毫秒");
    }
}
(7)守护线程(Daemon Thread):当其他的非守护线程执行完毕后,守护线程会陆陆续续结束
package com.yuan.threadMethed;

public class Demo03 {
    public static void main(String[] args) {
        Thread t1 = new Thread("女神") {
            @Override
            public void run() {
                for (int i = 'a'; i < 'z'; i++) {
                    System.out.println(getName() + " : " + i);
                }
            }
        };
        Thread t2 = new Thread("备胎") {
            @Override
            public void run() {
                for (int i = 1; i < 100; i++) {
                    System.out.println(getName() + " : " + i);
                }
            }
        };
        //t2线程设置为守护线程
        //细节:当其他的非守护线程执行完毕后,守护线程会陆陆续续结束
        t2.setDaemon(true);
        t1.start();
        t2.start();

    }
}

三、多线程的同步

1、锁:

       每个Java对象都可以充当一个实现同步的锁,这些锁被称为内置锁(Intrinsic Lock)或者监视器锁(Monitor Lock)。 

2、同步锁:

   Synchronized同步锁,简单来说,使用Synchronized关键字将一段代码逻辑,用一把锁给锁起来,只有获得了这把锁的线程才访问。并且同一时刻, 只有一个线程能持有这把锁, 这样就保证了同一时刻只有一个线程能执行被锁住的代码,从而确保代码的线程安全。

3、多线程的数据不一致:

       当多个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。这个时候,一个在单线程模型下不存在的问题就会发生:如果多个线程同时读写共享变量,会出现数据不一致的问题。

public class Main {
    public static void main(String[] args) throws Exception {
        Thread add = new AddThread();
        Thread dec = new DecThread();
        add.start();
        dec.start();
        add.join();
        dec.join();
        System.out.println(Counter.count);
    }
}

class Counter {
    public static int count = 0;
}

class AddThread extends Thread {
    public void run() {
        for (int i=0; i<10000; i++) { Counter.count += 1; }
    }
}

class DecThread extends Thread {
    public void run() {
        for (int i=0; i<10000; i++) { Counter.count -= 1; }
    }
}

在上述代码案例中,两个线程同时对一个int变量进行操作,一个加10000次,一个减10000次,最后结果应该是0,但是,每次运行,结果实际上都是不一样的。

这是因为对变量进行读取和写入时,结果要正确,必须保证是原子操作原子操作是指不能被中断的一个或一系列操作。

        通过加锁和解锁的操作,就能保证3条指令总是在一个线程执行期间,不会有其他线程会进入此指令区间。即使在执行期线程被操作系统中断执行,其他线程也会因为无法获得锁导致无法进入此指令区间。只有执行线程将锁释放后,其他线程才有机会获得锁并执行。这种加锁和解锁之间的代码块我们称之为临界区(Critical Section),任何时候临界区最多只有一个线程能执行。  

4、synchronized 关键字的用法:

(1)修饰实例方法:
  • 使用当前对象this充当锁完成对当前方法的锁定,只有获取this锁的线程才能访问当前方法;
  • 并发过程中,同一时刻,可以有N个线程请求执行方法,但只有一个线程可以持有this锁,才能执行;
  • 不同线程,持有的对象,必须相同;
public class Foo {
    // 实例方法
    public synchronized void doSth1() {
        // 获取this锁,才能执行该方法
    }
    
    // 实例方法
    public void doSth2() {
        synchronized(this) {
           // 获取this锁,才能执行该代码块
        }
    }
}
(2)修饰静态代码块:
  • 使用当前对象的Class对象充当锁,完成对当前方法的锁定,只有获取Class锁的线程才能访问当前方法;
  • 不同线程,持有的对象,可以不同,但必须相同class类型;
public static void main(String[] args) {
    // 创建不同的对象(相同类型)
    Foo fa = new Foo();
    Foo fb = new Foo();

    // 创建不同线程1
    Thread thread01 = new Thread() {
        public void run() {
            // 使用不同的对象访问synchronized方法
            fa.doSth2();
        }
    };

    // 创建不同线程2
    Thread thread02 = new Thread() {
        public void run() {
            // 使用不同的对象访问synchronized方法
            fb.doSth2();
        }
    };

    // 启动线程
    thread01.start();
    thread02.start();
}
class Foo {
    // 静态方法
    public synchronized static void doSth1() {
        // 获取当前对象的Class对象锁,才能执行该方法
    }
    
    // 实例方法
    public static void doSth2() {
        synchronized(this.getClass()) {
        	// 获取当前对象的Class对象锁,才能执行该代码块
        }
    }
}
(3)修饰代码块:

/**1.锁代码块:
 * 锁对象可以是任意类型的对象,必须要保证多条线程公用一个锁对象
 * synchronized(锁对象) {
 *      操作的共享的代码
 * }
 * 某条线程获取到了锁资源,锁关闭,当里面的任务执行完成,锁释放
 * 默认情况下,锁是打开状态

*/

package com.yuan.synchronizedclasss;

public class Demo02 {
    public static void main(String[] args) {
        ShoeMai t1=new ShoeMai("窗口1");
        ShoeMai t2=new ShoeMai("窗口2");
        ShoeMai t3=new ShoeMai("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
class ShoeMai extends Thread{
    public  static int ticket=0;
    public static Object obj=new Object();
    public ShoeMai(String name) {
        super(name);
    }

    @Override
    public void run() {
       while (true){
           synchronized (obj) {
               if (ticket >= 100) {
                   break;
               }
               System.out.println(getName() + "正在售卖第" + ++ticket + "张票");
               try {
                   Thread.sleep(100);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        }
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呼哧呼哧.

栓Q!!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值