1.线程
1.抢占式运行
CPU在执行的时候,按照时间片来执行的,单位的时间片是抢占是执行,比如 idea qq 抢占CPU的,比如qq的线程抢到cpu,idea线程等待
2.资源共享性
一个线程可以共享当前CPU, 网卡等
Java程序:
一个Java程序就是一个进程 Demo1 就是一个应用程序 就是一个进程
一个Java程序Demo1里面至少 几个线程?
两个:
main主函数线程
JVM垃圾回收器线程
2.线程和进程的区别【面试题】
进程是一个应用程序,是独立的 线程是进程中最小的基本单位 线程有独立性和互斥性 线程有抢占式资源共享特性
3.并发和并行
并发:同时发生,轮流交替执行
并行:真正意义上的同时执行
4.创建线程的两种方式【重点】
1.一个是将一个类声明为Thread的子类,这个子类应该重写run方法,然后可以分配并启动子类的实例
package com.qfedu.a_thread; class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("线程1吃水煮肉片" + i); } } } class MyThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("线程二吃毛血旺" + i); } } } public class Demo1 { public static void main(String[] args) { MyThread myThread1 = new MyThread(); myThread1.start();//启动线程 start方法,在主线程中开启子线程 MyThread1 myThread2 = new MyThread1(); myThread2.start(); for (int i = 0; i < 100; i++) { System.out.println("主函数线程吃大盘鸡" + i); } } }
2.创建一个类实现Runnable接口,然后实现run方法,可以分配类的实例,在创建Thread时作为参数传递,并启动
package com.qfedu.a_thread; class MyThread3 implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("线程3 " + i); } } } class MyThread4 implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("线程4 " + i); } } } public class Demo2 { public static void main(String[] args) { MyThread3 myThread3 = new MyThread3(); //Thread(Runnable target) 参数是Ruannable这个接口对象 //分配一个新的Thread对象 Thread thread = new Thread(myThread3); thread.start(); MyThread4 myThread4 = new MyThread4(); Thread thread1 = new Thread(myThread4); thread1.start(); for (int i = 0; i < 100; i++) { System.out.println("主线程" + i); } } }
5.线程下面的几个方法
构造方法
Thread()
分配一个新的Thread
对象。 无参构造方法Thread(Runnable target)
分配一个新的Thread
对象。 有参构造Thread(Runnable target, String name)
分配一个新的Thread
对象。并起名字
线程方法:
static Thread
currentThread()
返回对当前正在执行的线程对象的引用String
getName()
返回此线程的名称。void
setName(String name)
将此线程的名称更改为等于参数name
。int
getPriority()
返回此线程的优先级。void
setPriority(int newPriority)
更改此线程的优先级。设置优先并不一定优先,只是增加了执行的概率。最小值是1,最大值是10,默认的是5 static void
sleep(long millis)
使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
package com.qfedu.threadfun; class MyThread1 implements Runnable{ @Override public void run() { Thread thread = Thread.currentThread(); System.out.println(thread.getName()); System.out.println(thread.getPriority()); thread.setName("线程1"); thread.setPriority(1); System.out.println(thread.getName()); System.out.println(thread.getPriority()); // for (int i = 0; i < 500; i++) { // System.out.println(thread.getName() + " " + i); // } } } class MyThread2 implements Runnable{ @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Thread thread = Thread.currentThread(); System.out.println(thread.getName()); System.out.println(thread.getPriority()); thread.setName("线程2"); thread.setPriority(10); System.out.println(thread.getName()); System.out.println(thread.getPriority()); // for (int i = 0; i < 500; i++) { // System.out.println(thread.getName() + " " + i); // } } } public class Demo1 { public static void main(String[] args) { new Thread(new MyThread1()).start(); new Thread(new MyThread2()).start(); Thread thread = new Thread(); System.out.println(thread.getName()); System.out.println(thread.getPriority()); } }
6.线程的的锁和同步【重要】
为什么要进行线程的同步?
Java是允许多线程(多个线程),当多个线程操作同一个资源(咋操作)的时候,会导致得到或者打印的数据不准确。从而发生冲突。咋解决?加同步锁。
美团 淘票票 这个两个线程,都去麦同一场次的票 结果美团卖出去一张1排1列的票 结果淘票票也卖出去了1排1列的票 你感觉合适吗? 就是上面的这种结果!!!不合适的,分享同一个资源的时候,要保证分享资源的数据,合法性!!! 分析结果:
package com.qfedu.c_sync; //最理想的状态!!! //先线程1进入到ticket=50,循环 循环结束以后 此时 //tiket=49了 //循环第二次的时候 线程2抢到资源了 此时ticket=49 //循环 打印49 tiket-- ticket=48了 //循环第三次的时候 线程2 抢到资源了, 此时ticket=48 //打印卖第48张票。ticket-- tiket=47 //线程1又抢到循环 //现在的情况是:有可能两个线程同时进入到while循环 // class MySync implements Runnable { int ticket = 50; @Override public void run() { // while (true) {//死循环 //两个线程都进入到了循环了 //此时两个线程所持有的ticket 都是50 //但是两个线程都要往下执行 //有可能线程1 先执行了sout(50) 线程2在等待哦!!! //线程1执行了--操作并出了循环 线程1ticket = 49 //线程1又抢到循环了 sout(49) tiket-- //再进入倒这个循环,有可能线程2抢到这个执行权 //线程2要往下执行输出语句 ticket=50 打印50 if (ticket > 0) { //线程具有抢占式的运行 //咱们有没有可能,线程3进入到if语句 //此时线程1也进入到if语句了 //线程3去打印 卖出了50张票 //在线程1里面 ticket=50 //线程3又抢到ticket-- 又进入到循环了 ticket = 49 //线程3又抢到了ticket-- 又进入倒循环 ticket=48 //线程1又抢到资源要执行,执行输出语句 tiekct=50 System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票"); ticket--; } else { System.out.println("买完了"); break;//终止循环!!! } } } } public class Demo1 { public static void main(String[] args) { MySync mySync = new MySync(); //这三个线程 Thread thread1 = new Thread(mySync, "线程1"); thread1.start(); Thread thread2 = new Thread(mySync, "线程2"); thread2.start(); Thread thread3 = new Thread(mySync, "线程3"); thread3.start(); } }
解决方案:
1.同步方法:使用一个关键字synchronized修饰方法。因为Java对象都有一个内置的锁对象。当使用这个关键字的时候,修饰方法的时候,这个方法就会被锁保护起来被锁锁住
当一个线程进来以后,会立马锁住当前的方法。意味着只有一个线程进来,其他线程都在外面等着。
public synchronized void run () { }
package com.qfedu.c_sync; //最理想的状态!!! //先线程1进入到ticket=50,循环 循环结束以后 此时 //tiket=49了 //循环第二次的时候 线程2抢到资源了 此时ticket=49 //循环 打印49 tiket-- ticket=48了 //循环第三次的时候 线程2 抢到资源了, 此时ticket=48 //打印卖第48张票。ticket-- tiket=47 //线程1又抢到循环 //现在的情况是:有可能两个线程同时进入到while循环 // class MySync1 implements Runnable { int ticket = 50; //对这个run方法加了锁 就意味着只有一个线程进入到run方法中 //其他线程都在run方法外面等待 @Override public synchronized void run() { // while (true) {//死循环 //两个线程都进入到了循环了 //此时两个线程所持有的ticket 都是50 //但是两个线程都要往下执行 //有可能线程1 先执行了sout(50) 线程2在等待哦!!! //线程1执行了--操作并出了循环 线程1ticket = 49 //线程1又抢到循环了 sout(49) tiket-- //再进入倒这个循环,有可能线程2抢到这个执行权 //线程2要往下执行输出语句 ticket=50 打印50 if (ticket > 0) { //线程具有抢占式的运行 //咱们有没有可能,线程3进入到if语句 //此时线程1也进入到if语句了 //线程3去打印 卖出了50张票 //在线程1里面 ticket=50 //线程3又抢到ticket-- 又进入到循环了 ticket = 49 //线程3又抢到了ticket-- 又进入倒循环 ticket=48 //线程1又抢到资源要执行,执行输出语句 tiekct=50 System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票"); ticket--; } else { System.out.println("买完了"); break;//终止循环!!! } } } } public class Demo2 { public static void main(String[] args) { MySync1 mySync = new MySync1(); //这三个线程 Thread thread1 = new Thread(mySync, "线程1"); thread1.start(); Thread thread2 = new Thread(mySync, "线程2"); thread2.start(); Thread thread3 = new Thread(mySync, "线程3"); thread3.start(); //为啥都是线程1卖出去的票? //很巧 线程1抢到执行权了,进入到run方法中 //线程2和线程3在外面等着。 //一个循环进来以后,把循环全部执行完!!! //会出现一家独大的情况!!!也是不符合咱们生活场景的!!! //咋解决?咱们 不能方法中加锁,在其他地方加锁 } } 换另外一种解决方法:
同步代码块:就是拥有了synchronized 关键字修饰一个语句块。被修饰的语句块会被加锁。从而实现同步。
语法格式:
synchronized (this) { 被加锁的代码块 }package com.qfedu.c_sync; //最理想的状态!!! //先线程1进入到ticket=50,循环 循环结束以后 此时 //tiket=49了 //循环第二次的时候 线程2抢到资源了 此时ticket=49 //循环 打印49 tiket-- ticket=48了 //循环第三次的时候 线程2 抢到资源了, 此时ticket=48 //打印卖第48张票。ticket-- tiket=47 //线程1又抢到循环 //现在的情况是:有可能两个线程同时进入到while循环 // class MySync2 implements Runnable { int ticket = 500; //对这个run方法加了锁 就意味着只有一个线程进入到run方法中 //其他线程都在run方法外面等待 @Override public void run() { //能不能对循环加锁?不能 因为循环加锁以后,还是一个线程循环完,没有任何意义 while (true) {//死循环 //if语句加了锁以后 //就意味着只有一个线程进入到if语句 //假如线程1进入if语句了,线程2和线程3就会等待 //线程1打印50 并-- ticket变量为49 //线程2抢到了49 sout(49) tiket-- 48 //其他线程再抢!!! //核心业务 加了锁,只让一个线程进入,操作完以后。锁释放掉 //然后这三个线程再抢。还只能进一个,再操作核心业务 synchronized (this) {//只能让一个线程进入操作,其他线程在外面等待排队 if (ticket > 0) { //线程具有抢占式的运行 //咱们有没有可能,线程3进入到if语句 //此时线程1也进入到if语句了 //线程3去打印 卖出了50张票 //在线程1里面 ticket=50 //线程3又抢到ticket-- 又进入到循环了 ticket = 49 //线程3又抢到了ticket-- 又进入倒循环 ticket=48 //线程1又抢到资源要执行,执行输出语句 tiekct=50 System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票"); ticket--; } else { System.out.println("买完了"); break;//终止循环!!! } } } } } public class Demo3 { public static void main(String[] args) { MySync2 mySync = new MySync2(); //这三个线程 Thread thread1 = new Thread(mySync, "线程1"); thread1.start(); Thread thread2 = new Thread(mySync, "线程2"); thread2.start(); Thread thread3 = new Thread(mySync, "线程3"); thread3.start(); //为啥都是线程1卖出去的票? //很巧 线程1抢到执行权了,进入到run方法中 //线程2和线程3在外面等着。 //一个循环进来以后,把循环全部执行完!!! //会出现一家独大的情况!!!也是不符合咱们生活场景的!!! //咋解决?咱们 不能方法中加锁,在其他地方加锁 } }
线程就是这样,不可控制,但是可以加锁。让他可控制。